summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKamil Trzciński <ayufan@ayufan.eu>2018-02-28 21:28:43 +0100
committerKamil Trzciński <ayufan@ayufan.eu>2018-02-28 21:28:43 +0100
commita2f375e8f74870dcdcfa1c7886bd1c14c80a684e (patch)
tree6b6e3a4f7554f4671edc17d87869dd6916984404
parenta22f6fa6e50bb31921415b01fd345d6802581390 (diff)
parent81852d1f902c2923c239e9c33cab77f5fd6ca8d8 (diff)
downloadgitlab-ce-object-storage-ee-to-ce-backport.tar.gz
Merge remote-tracking branch 'origin/master' into object-storage-ee-to-ce-backportobject-storage-ee-to-ce-backport
-rw-r--r--.codeclimate.yml2
-rw-r--r--.eslintrc2
-rw-r--r--.gitlab-ci.yml148
-rw-r--r--.rubocop.yml28
-rw-r--r--CHANGELOG.md236
-rw-r--r--CONTRIBUTING.md8
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock18
-rw-r--r--PROCESS.md13
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/api.js4
-rw-r--r--app/assets/javascripts/awards_handler.js6
-rw-r--r--app/assets/javascripts/behaviors/copy_to_clipboard.js2
-rw-r--r--app/assets/javascripts/behaviors/quick_submit.js2
-rw-r--r--app/assets/javascripts/behaviors/requires_input.js2
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js12
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js2
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue (renamed from app/assets/javascripts/boards/components/board_new_issue.js)91
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js3
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.js5
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js9
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.js5
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js3
-rw-r--r--app/assets/javascripts/boards/index.js (renamed from app/assets/javascripts/boards/boards_bundle.js)14
-rw-r--r--app/assets/javascripts/boards/models/issue.js2
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js2
-rw-r--r--app/assets/javascripts/ci_variable_list/ajax_variable_list.js1
-rw-r--r--app/assets/javascripts/ci_variable_list/ci_variable_list.js6
-rw-r--r--app/assets/javascripts/commit/image_file.js1
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_bundle.js4
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue1
-rw-r--r--app/assets/javascripts/commits.js72
-rw-r--r--app/assets/javascripts/commons/bootstrap.js4
-rw-r--r--app/assets/javascripts/commons/jquery.js2
-rw-r--r--app/assets/javascripts/compare.js2
-rw-r--r--app/assets/javascripts/compare_autocomplete.js4
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js1
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_review_component.vue2
-rw-r--r--app/assets/javascripts/deploy_keys/index.js4
-rw-r--r--app/assets/javascripts/diff.js11
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js6
-rw-r--r--app/assets/javascripts/dispatcher.js703
-rw-r--r--app/assets/javascripts/docs/docs_bundle.js7
-rw-r--r--app/assets/javascripts/droplab/constants.js2
-rw-r--r--app/assets/javascripts/droplab/drop_down.js13
-rw-r--r--app/assets/javascripts/due_date_select.js6
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue31
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue2
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js4
-rw-r--r--app/assets/javascripts/files_comment_button.js2
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js101
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue104
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_emoji.js10
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js16
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_non_user.js12
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js14
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js20
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown.js15
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js40
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js68
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_tokenizer.js5
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js22
-rw-r--r--app/assets/javascripts/filtered_search/recent_searches_root.js4
-rw-r--r--app/assets/javascripts/gl_dropdown.js20
-rw-r--r--app/assets/javascripts/gl_form.js7
-rw-r--r--app/assets/javascripts/gpg_badges.js17
-rw-r--r--app/assets/javascripts/graphs/graphs_bundle.js4
-rw-r--r--app/assets/javascripts/groups/components/item_stats_value.vue10
-rw-r--r--app/assets/javascripts/groups_select.js26
-rw-r--r--app/assets/javascripts/help/help.js12
-rw-r--r--app/assets/javascripts/how_to_merge.js25
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue6
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue2
-rw-r--r--app/assets/javascripts/ide/monaco_loader.js5
-rw-r--r--app/assets/javascripts/importer_status.js117
-rw-r--r--app/assets/javascripts/integrations/index.js7
-rw-r--r--app/assets/javascripts/integrations/integration_settings_form.js4
-rw-r--r--app/assets/javascripts/issue.js76
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue1
-rw-r--r--app/assets/javascripts/issue_status_select.js2
-rw-r--r--app/assets/javascripts/jobs/job_details_bundle.js4
-rw-r--r--app/assets/javascripts/labels_select.js26
-rw-r--r--app/assets/javascripts/layout_nav.js2
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js10
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js2
-rw-r--r--app/assets/javascripts/line_highlighter.js2
-rw-r--r--app/assets/javascripts/main.js14
-rw-r--r--app/assets/javascripts/members.js4
-rw-r--r--app/assets/javascripts/merge_request.js2
-rw-r--r--app/assets/javascripts/merge_request_tabs.js2
-rw-r--r--app/assets/javascripts/milestone_select.js24
-rw-r--r--app/assets/javascripts/notes.js34
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue61
-rw-r--r--app/assets/javascripts/notes/index.js2
-rw-r--r--app/assets/javascripts/notes/services/notes_service.js3
-rw-r--r--app/assets/javascripts/notes/stores/actions.js33
-rw-r--r--app/assets/javascripts/notes/stores/getters.js1
-rw-r--r--app/assets/javascripts/notes/stores/mutation_types.js4
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js8
-rw-r--r--app/assets/javascripts/notifications_dropdown.js4
-rw-r--r--app/assets/javascripts/pager.js2
-rw-r--r--app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js12
-rw-r--r--app/assets/javascripts/pages/admin/abuse_reports/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/admin.js4
-rw-r--r--app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js6
-rw-r--r--app/assets/javascripts/pages/admin/broadcast_messages/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/cohorts/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/conversational_development_index/show/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/groups/edit/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/groups/new/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/groups/show/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/impersonation_tokens/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue26
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/index.js43
-rw-r--r--app/assets/javascripts/pages/admin/labels/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/labels/new/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/projects/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/projects/index/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue (renamed from app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue)0
-rw-r--r--app/assets/javascripts/pages/admin/users/index.js (renamed from app/assets/javascripts/pages/admin/users/shared/index.js)4
-rw-r--r--app/assets/javascripts/pages/ci/lints/ci_lint_editor.js7
-rw-r--r--app/assets/javascripts/pages/ci/lints/create/index.js3
-rw-r--r--app/assets/javascripts/pages/ci/lints/index.js3
-rw-r--r--app/assets/javascripts/pages/ci/lints/show/index.js3
-rw-r--r--app/assets/javascripts/pages/dashboard/activity/index.js2
-rw-r--r--app/assets/javascripts/pages/dashboard/groups/index/index.js6
-rw-r--r--app/assets/javascripts/pages/dashboard/issues/index.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/merge_requests/index.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/milestones/show/index.js6
-rw-r--r--app/assets/javascripts/pages/dashboard/projects/index.js2
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/index.js2
-rw-r--r--app/assets/javascripts/pages/explore/groups/index.js4
-rw-r--r--app/assets/javascripts/pages/explore/projects/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/activity/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/edit/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/labels/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/labels/index/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/labels/new/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/milestones/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/milestones/new/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/milestones/show/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/new/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js2
-rw-r--r--app/assets/javascripts/pages/help/index.js3
-rw-r--r--app/assets/javascripts/pages/help/index/index.js7
-rw-r--r--app/assets/javascripts/pages/help/show/index.js3
-rw-r--r--app/assets/javascripts/pages/help/ui/index.js3
-rw-r--r--app/assets/javascripts/pages/import/fogbugz/new_user_map/index.js4
-rw-r--r--app/assets/javascripts/pages/import/gitlab_projects/new/index.js3
-rw-r--r--app/assets/javascripts/pages/milestones/shared/init_milestones_show.js2
-rw-r--r--app/assets/javascripts/pages/omniauth_callbacks/index.js4
-rw-r--r--app/assets/javascripts/pages/profiles/accounts/show/index.js3
-rw-r--r--app/assets/javascripts/pages/profiles/index/index.js4
-rw-r--r--app/assets/javascripts/pages/profiles/personal_access_tokens/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/activity/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/artifacts/browse/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/artifacts/file/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/blame/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/blob/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/blob/new/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/blob/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/boards/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/branches/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/branches/new/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/clusters/destroy/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/clusters/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/clusters/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/clusters/update/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/commit/pipelines/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/commits/show/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/compare/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/compare/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/edit/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/environments/folder/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/environments/metrics/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/find_file/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/graphs/charts/index.js (renamed from app/assets/javascripts/graphs/graphs_charts.js)2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/index.js (renamed from app/assets/javascripts/graphs/graphs_show.js)6
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js (renamed from app/assets/javascripts/graphs/stat_graph_contributors.js)2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js (renamed from app/assets/javascripts/graphs/stat_graph_contributors_graph.js)2
-rw-r--r--app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js (renamed from app/assets/javascripts/graphs/stat_graph_contributors_util.js)0
-rw-r--r--app/assets/javascripts/pages/projects/imports/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/init_blob.js3
-rw-r--r--app/assets/javascripts/pages/projects/issues/edit/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/new/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/show/index.js10
-rw-r--r--app/assets/javascripts/pages/projects/jobs/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/labels/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/labels/index/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/labels/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/index.js (renamed from app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js)2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/show/index.js30
-rw-r--r--app/assets/javascripts/pages/projects/milestones/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/milestones/index/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/milestones/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/milestones/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/new/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js (renamed from app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js)2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue (renamed from app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue)0
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue (renamed from app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue)2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js (renamed from app/assets/javascripts/pipeline_schedules/components/target_branch_dropdown.js)0
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js (renamed from app/assets/javascripts/pipeline_schedules/components/timezone_dropdown.js)0
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg (renamed from app/assets/javascripts/pipeline_schedules/icons/intro_illustration.svg)0
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js (renamed from app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js)10
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/builds/index.js17
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/charts/index.js56
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/failures/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/init_pipelines.js16
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/new/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/pipelines/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/project.js8
-rw-r--r--app/assets/javascripts/pages/projects/project_members/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/releases/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/services/edit/index.js13
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/settings/repository/show/index.js6
-rw-r--r--app/assets/javascripts/pages/projects/snippets/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/snippets/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/snippets/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/tags/new/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/tree/show/index.js9
-rw-r--r--app/assets/javascripts/pages/projects/wikis/index.js4
-rw-r--r--app/assets/javascripts/pages/search/init_filtered_search.js6
-rw-r--r--app/assets/javascripts/pages/search/show/index.js2
-rw-r--r--app/assets/javascripts/pages/search/show/search.js6
-rw-r--r--app/assets/javascripts/pages/sessions/index.js4
-rw-r--r--app/assets/javascripts/pages/snippets/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/snippets/new/index.js2
-rw-r--r--app/assets/javascripts/pages/snippets/show/index.js11
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js (renamed from app/assets/javascripts/users/activity_calendar.js)0
-rw-r--r--app/assets/javascripts/pages/users/index.js (renamed from app/assets/javascripts/users/index.js)2
-rw-r--r--app/assets/javascripts/pages/users/show/index.js3
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js (renamed from app/assets/javascripts/users/user_tabs.js)10
-rw-r--r--app/assets/javascripts/pipelines/components/async_button.vue30
-rw-r--r--app/assets/javascripts/pipelines/components/graph/graph_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue1
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue71
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue13
-rw-r--r--app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue65
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue65
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js10
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_mediator.js (renamed from app/assets/javascripts/pipelines/pipeline_details_mediatior.js)3
-rw-r--r--app/assets/javascripts/pipelines/pipelines_charts.js38
-rw-r--r--app/assets/javascripts/pipelines/pipelines_times.js27
-rw-r--r--app/assets/javascripts/profile/account/index.js48
-rw-r--r--app/assets/javascripts/profile/profile.js18
-rw-r--r--app/assets/javascripts/project_find_file.js4
-rw-r--r--app/assets/javascripts/project_select.js14
-rw-r--r--app/assets/javascripts/projects/project_import_gitlab_project.js8
-rw-r--r--app/assets/javascripts/projects/project_new.js31
-rw-r--r--app/assets/javascripts/prometheus_metrics/index.js6
-rw-r--r--app/assets/javascripts/prometheus_metrics/prometheus_metrics.js2
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js4
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_create.js2
-rw-r--r--app/assets/javascripts/protected_branches/protected_branch_edit.js4
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js4
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_create.js2
-rw-r--r--app/assets/javascripts/protected_tags/protected_tag_edit.js2
-rw-r--r--app/assets/javascripts/ref_select_dropdown.js4
-rw-r--r--app/assets/javascripts/render_gfm.js1
-rw-r--r--app/assets/javascripts/render_math.js44
-rw-r--r--app/assets/javascripts/render_mermaid.js3
-rw-r--r--app/assets/javascripts/right_sidebar.js14
-rw-r--r--app/assets/javascripts/search_autocomplete.js25
-rw-r--r--app/assets/javascripts/settings_panels.js2
-rw-r--r--app/assets/javascripts/shortcuts_issuable.js7
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue7
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue (renamed from app/assets/javascripts/sidebar/components/time_tracking/time_tracker.js)149
-rw-r--r--app/assets/javascripts/sidebar/lib/sidebar_move_issue.js2
-rw-r--r--app/assets/javascripts/sidebar/mount_milestone_sidebar.js27
-rw-r--r--app/assets/javascripts/single_file_diff.js6
-rw-r--r--app/assets/javascripts/star.js25
-rw-r--r--app/assets/javascripts/subscription_select.js2
-rw-r--r--app/assets/javascripts/task_list.js2
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js6
-rw-r--r--app/assets/javascripts/ui_development_kit.js4
-rw-r--r--app/assets/javascripts/user_callout.js2
-rw-r--r--app/assets/javascripts/users_select.js84
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js43
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue62
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js19
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue25
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js18
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue24
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js3
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue5
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js9
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/confirmation_input.vue62
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue106
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue15
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_button.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/table_pagination.vue6
-rw-r--r--app/assets/stylesheets/framework.scss14
-rw-r--r--app/assets/stylesheets/framework/broadcast_messages.scss (renamed from app/assets/stylesheets/framework/broadcast-messages.scss)0
-rw-r--r--app/assets/stylesheets/framework/buttons.scss13
-rw-r--r--app/assets/stylesheets/framework/common.scss2
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss (renamed from app/assets/stylesheets/framework/contextual-sidebar.scss)0
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss4
-rw-r--r--app/assets/stylesheets/framework/emoji_sprites.scss (renamed from app/assets/stylesheets/framework/emoji-sprites.scss)0
-rw-r--r--app/assets/stylesheets/framework/forms.scss3
-rw-r--r--app/assets/stylesheets/framework/gitlab_theme.scss (renamed from app/assets/stylesheets/framework/gitlab-theme.scss)0
-rw-r--r--app/assets/stylesheets/framework/lists.scss2
-rw-r--r--app/assets/stylesheets/framework/mobile.scss4
-rw-r--r--app/assets/stylesheets/framework/page_header.scss (renamed from app/assets/stylesheets/framework/page-header.scss)0
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss (renamed from app/assets/stylesheets/framework/secondary-navigation-elements.scss)0
-rw-r--r--app/assets/stylesheets/framework/selects.scss2
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss1
-rw-r--r--app/assets/stylesheets/framework/stacked_progress_bar.scss (renamed from app/assets/stylesheets/framework/stacked-progress-bar.scss)2
-rw-r--r--app/assets/stylesheets/framework/typography.scss6
-rw-r--r--app/assets/stylesheets/framework/variables.scss7
-rw-r--r--app/assets/stylesheets/pages/commits.scss8
-rw-r--r--app/assets/stylesheets/pages/environments.scss4
-rw-r--r--app/assets/stylesheets/pages/events.scss1
-rw-r--r--app/assets/stylesheets/pages/issuable.scss14
-rw-r--r--app/assets/stylesheets/pages/issues.scss88
-rw-r--r--app/assets/stylesheets/pages/labels.scss12
-rw-r--r--app/assets/stylesheets/pages/milestone.scss4
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss9
-rw-r--r--app/assets/stylesheets/pages/projects.scss59
-rw-r--r--app/assets/stylesheets/pages/todos.scss1
-rw-r--r--app/controllers/application_controller.rb11
-rw-r--r--app/controllers/boards/issues_controller.rb2
-rw-r--r--app/controllers/concerns/controller_with_cross_project_access_check.rb24
-rw-r--r--app/controllers/concerns/issuable_collections.rb4
-rw-r--r--app/controllers/concerns/issues_action.rb8
-rw-r--r--app/controllers/concerns/merge_requests_action.rb7
-rw-r--r--app/controllers/concerns/routable_actions.rb8
-rw-r--r--app/controllers/concerns/uploads_actions.rb5
-rw-r--r--app/controllers/dashboard/application_controller.rb4
-rw-r--r--app/controllers/dashboard/groups_controller.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb1
-rw-r--r--app/controllers/dashboard/snippets_controller.rb2
-rw-r--r--app/controllers/groups/application_controller.rb6
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/children_controller.rb1
-rw-r--r--app/controllers/groups/group_members_controller.rb4
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb1
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/groups_controller.rb7
-rw-r--r--app/controllers/import/base_controller.rb24
-rw-r--r--app/controllers/import/bitbucket_controller.rb22
-rw-r--r--app/controllers/import/fogbugz_controller.rb16
-rw-r--r--app/controllers/import/github_controller.rb19
-rw-r--r--app/controllers/import/gitlab_controller.rb18
-rw-r--r--app/controllers/import/google_code_controller.rb16
-rw-r--r--app/controllers/oauth/applications_controller.rb3
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb4
-rw-r--r--app/controllers/projects/blob_controller.rb2
-rw-r--r--app/controllers/projects/clusters/gcp_controller.rb14
-rw-r--r--app/controllers/projects/issues_controller.rb8
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb6
-rw-r--r--app/controllers/projects/merge_requests_controller.rb5
-rw-r--r--app/controllers/projects/pages_domains_controller.rb18
-rw-r--r--app/controllers/projects/prometheus/metrics_controller.rb27
-rw-r--r--app/controllers/projects/prometheus_controller.rb24
-rw-r--r--app/controllers/projects_controller.rb9
-rw-r--r--app/controllers/search_controller.rb9
-rw-r--r--app/controllers/users_controller.rb20
-rw-r--r--app/finders/autocomplete_users_finder.rb22
-rw-r--r--app/finders/concerns/finder_methods.rb51
-rw-r--r--app/finders/concerns/finder_with_cross_project_access.rb70
-rw-r--r--app/finders/events_finder.rb4
-rw-r--r--app/finders/issuable_finder.rb122
-rw-r--r--app/finders/issues_finder.rb47
-rw-r--r--app/finders/labels_finder.rb19
-rw-r--r--app/finders/members_finder.rb57
-rw-r--r--app/finders/merge_request_target_project_finder.rb2
-rw-r--r--app/finders/milestones_finder.rb2
-rw-r--r--app/finders/notes_finder.rb2
-rw-r--r--app/finders/snippets_finder.rb75
-rw-r--r--app/finders/todos_finder.rb5
-rw-r--r--app/finders/user_recent_events_finder.rb33
-rw-r--r--app/helpers/application_helper.rb32
-rw-r--r--app/helpers/application_settings_helper.rb1
-rw-r--r--app/helpers/avatars_helper.rb14
-rw-r--r--app/helpers/blob_helper.rb143
-rw-r--r--app/helpers/branches_helper.rb6
-rw-r--r--app/helpers/dashboard_helper.rb24
-rw-r--r--app/helpers/diff_helper.rb2
-rw-r--r--app/helpers/explore_helper.rb16
-rw-r--r--app/helpers/groups_helper.rb40
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/issues_helper.rb21
-rw-r--r--app/helpers/namespaces_helper.rb2
-rw-r--r--app/helpers/nav_helper.rb32
-rw-r--r--app/helpers/preferences_helper.rb26
-rw-r--r--app/helpers/projects_helper.rb115
-rw-r--r--app/helpers/tree_helper.rb8
-rw-r--r--app/helpers/users_helper.rb14
-rw-r--r--app/helpers/webpack_helper.rb15
-rw-r--r--app/mailers/emails/members.rb11
-rw-r--r--app/mailers/emails/pages_domains.rb43
-rw-r--r--app/mailers/notify.rb1
-rw-r--r--app/models/ability.rb30
-rw-r--r--app/models/blob.rb2
-rw-r--r--app/models/chat_name.rb21
-rw-r--r--app/models/ci/build.rb3
-rw-r--r--app/models/ci/group_variable.rb5
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/ci/variable.rb5
-rw-r--r--app/models/commit.rb12
-rw-r--r--app/models/concerns/milestoneish.rb8
-rw-r--r--app/models/concerns/protected_ref_access.rb3
-rw-r--r--app/models/deployment.rb4
-rw-r--r--app/models/environment.rb2
-rw-r--r--app/models/event.rb4
-rw-r--r--app/models/external_issue.rb2
-rw-r--r--app/models/identity.rb9
-rw-r--r--app/models/issue.rb13
-rw-r--r--app/models/member.rb2
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/namespace.rb4
-rw-r--r--app/models/network/graph.rb7
-rw-r--r--app/models/notification_recipient.rb1
-rw-r--r--app/models/pages_domain.rb65
-rw-r--r--app/models/project.rb57
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/project_services/prometheus_service.rb20
-rw-r--r--app/models/project_services/slash_commands_service.rb6
-rw-r--r--app/models/repository.rb18
-rw-r--r--app/models/snippet.rb21
-rw-r--r--app/models/tree.rb20
-rw-r--r--app/models/user.rb15
-rw-r--r--app/policies/base_policy.rb3
-rw-r--r--app/policies/issuable_policy.rb13
-rw-r--r--app/policies/issue_policy.rb3
-rw-r--r--app/policies/merge_request_policy.rb2
-rw-r--r--app/policies/project_policy.rb29
-rw-r--r--app/presenters/project_presenter.rb338
-rw-r--r--app/serializers/group_child_entity.rb17
-rw-r--r--app/serializers/project_serializer.rb3
-rw-r--r--app/services/chat_names/find_user_service.rb4
-rw-r--r--app/services/ci/create_trace_artifact_service.rb30
-rw-r--r--app/services/clusters/gcp/finalize_creation_service.rb4
-rw-r--r--app/services/delete_merged_branches_service.rb18
-rw-r--r--app/services/groups/nested_create_service.rb10
-rw-r--r--app/services/issuable_base_service.rb16
-rw-r--r--app/services/issues/fetch_referenced_merge_requests_service.rb12
-rw-r--r--app/services/issues/move_service.rb28
-rw-r--r--app/services/labels/find_or_create_service.rb22
-rw-r--r--app/services/members/authorized_destroy_service.rb1
-rw-r--r--app/services/merge_requests/build_service.rb10
-rw-r--r--app/services/notification_service.rb49
-rw-r--r--app/services/projects/autocomplete_service.rb11
-rw-r--r--app/services/projects/create_from_template_service.rb10
-rw-r--r--app/services/projects/gitlab_projects_import_service.rb14
-rw-r--r--app/services/projects/update_pages_configuration_service.rb10
-rw-r--r--app/services/quick_actions/interpret_service.rb45
-rw-r--r--app/services/verify_pages_domain_service.rb107
-rw-r--r--app/uploaders/gitlab_uploader.rb4
-rw-r--r--app/uploaders/personal_file_uploader.rb6
-rw-r--r--app/validators/variable_duplicates_validator.rb23
-rw-r--r--app/views/admin/application_settings/_form.html.haml11
-rw-r--r--app/views/admin/jobs/index.html.haml7
-rw-r--r--app/views/admin/runners/index.html.haml5
-rw-r--r--app/views/admin/users/_user.html.haml2
-rw-r--r--app/views/admin/users/show.html.haml2
-rw-r--r--app/views/ci/lints/show.html.haml2
-rw-r--r--app/views/ci/runner/_how_to_setup_runner.html.haml28
-rw-r--r--app/views/ci/runner/_how_to_setup_shared_runner.html.haml3
-rw-r--r--app/views/ci/runner/_how_to_setup_specific_runner.html.haml26
-rw-r--r--app/views/ci/variables/_variable_row.html.haml2
-rw-r--r--app/views/devise/sessions/two_factor.html.haml2
-rw-r--r--app/views/discussions/_discussion.html.haml2
-rw-r--r--app/views/errors/access_denied.html.haml10
-rw-r--r--app/views/events/_event.atom.builder2
-rw-r--r--app/views/groups/issues.html.haml8
-rw-r--r--app/views/groups/labels/new.html.haml1
-rw-r--r--app/views/groups/merge_requests.html.haml3
-rw-r--r--app/views/help/index.html.haml2
-rw-r--r--app/views/help/show.html.haml2
-rw-r--r--app/views/help/ui.html.haml4
-rw-r--r--app/views/import/base/create.js.haml13
-rw-r--r--app/views/import/base/unauthorized.js.haml14
-rw-r--r--app/views/import/gitlab_projects/new.html.haml2
-rw-r--r--app/views/issues/_issue.atom.builder2
-rw-r--r--app/views/layouts/header/_default.html.haml29
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml89
-rw-r--r--app/views/layouts/nav/_explore.html.haml21
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml156
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml13
-rw-r--r--app/views/notify/pages_domain_disabled_email.html.haml15
-rw-r--r--app/views/notify/pages_domain_disabled_email.text.haml13
-rw-r--r--app/views/notify/pages_domain_enabled_email.html.haml11
-rw-r--r--app/views/notify/pages_domain_enabled_email.text.haml9
-rw-r--r--app/views/notify/pages_domain_verification_failed_email.html.haml17
-rw-r--r--app/views/notify/pages_domain_verification_failed_email.text.haml14
-rw-r--r--app/views/notify/pages_domain_verification_succeeded_email.html.haml13
-rw-r--r--app/views/notify/pages_domain_verification_succeeded_email.text.haml10
-rw-r--r--app/views/notify/pipeline_failed_email.html.haml6
-rw-r--r--app/views/notify/pipeline_success_email.html.haml6
-rw-r--r--app/views/profiles/_head.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml3
-rw-r--r--app/views/profiles/show.html.haml4
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml4
-rw-r--r--app/views/projects/_merge_request_fast_forward_settings.html.haml2
-rw-r--r--app/views/projects/_new_project_push_tip.html.haml11
-rw-r--r--app/views/projects/_readme.html.haml2
-rw-r--r--app/views/projects/_stat_anchor_list.html.haml8
-rw-r--r--app/views/projects/blob/_header.html.haml4
-rw-r--r--app/views/projects/blob/_upload.html.haml5
-rw-r--r--app/views/projects/blob/edit.html.haml1
-rw-r--r--app/views/projects/blob/new.html.haml1
-rw-r--r--app/views/projects/blob/show.html.haml4
-rw-r--r--app/views/projects/blob/viewers/_balsamiq.html.haml2
-rw-r--r--app/views/projects/blob/viewers/_notebook.html.haml4
-rw-r--r--app/views/projects/blob/viewers/_pdf.html.haml4
-rw-r--r--app/views/projects/blob/viewers/_sketch.html.haml4
-rw-r--r--app/views/projects/blob/viewers/_stl.html.haml2
-rw-r--r--app/views/projects/buttons/_koding.html.haml2
-rw-r--r--app/views/projects/buttons/_star.html.haml2
-rw-r--r--app/views/projects/clusters/_empty_state.html.haml2
-rw-r--r--app/views/projects/commit/_pipelines_list.haml3
-rw-r--r--app/views/projects/commit/show.html.haml4
-rw-r--r--app/views/projects/commits/_commit.atom.builder2
-rw-r--r--app/views/projects/commits/_commit.html.haml4
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml4
-rw-r--r--app/views/projects/diffs/_file.html.haml2
-rw-r--r--app/views/projects/edit.html.haml4
-rw-r--r--app/views/projects/empty.html.haml53
-rw-r--r--app/views/projects/environments/folder.html.haml3
-rw-r--r--app/views/projects/environments/index.html.haml4
-rw-r--r--app/views/projects/environments/metrics.html.haml1
-rw-r--r--app/views/projects/environments/terminal.html.haml2
-rw-r--r--app/views/projects/graphs/charts.html.haml4
-rw-r--r--app/views/projects/graphs/show.html.haml4
-rw-r--r--app/views/projects/issues/_discussion.html.haml2
-rw-r--r--app/views/projects/issues/_new_branch.html.haml57
-rw-r--r--app/views/projects/issues/index.html.haml1
-rw-r--r--app/views/projects/issues/show.html.haml9
-rw-r--r--app/views/projects/jobs/_user.html.haml2
-rw-r--r--app/views/projects/jobs/show.html.haml4
-rw-r--r--app/views/projects/merge_requests/_how_to_merge.html.haml3
-rw-r--r--app/views/projects/merge_requests/conflicts.html.haml4
-rw-r--r--app/views/projects/merge_requests/conflicts/show.html.haml4
-rw-r--r--app/views/projects/merge_requests/index.html.haml1
-rw-r--r--app/views/projects/merge_requests/show.html.haml1
-rw-r--r--app/views/projects/milestones/show.html.haml2
-rw-r--r--app/views/projects/network/show.html.haml2
-rw-r--r--app/views/projects/network/show.json.erb2
-rw-r--r--app/views/projects/new.html.haml22
-rw-r--r--app/views/projects/pages/_list.html.haml13
-rw-r--r--app/views/projects/pages_domains/show.html.haml25
-rw-r--r--app/views/projects/pipeline_schedules/_form.html.haml4
-rw-r--r--app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml2
-rw-r--r--app/views/projects/pipeline_schedules/index.html.haml4
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml12
-rw-r--r--app/views/projects/pipelines/charts.html.haml3
-rw-r--r--app/views/projects/pipelines/charts/_pipeline_times.haml3
-rw-r--r--app/views/projects/pipelines/charts/_pipelines.haml3
-rw-r--r--app/views/projects/pipelines/index.html.haml4
-rw-r--r--app/views/projects/protected_branches/_index.html.haml2
-rw-r--r--app/views/projects/protected_tags/_index.html.haml2
-rw-r--r--app/views/projects/registry/repositories/index.html.haml4
-rw-r--r--app/views/projects/repositories/_feed.html.haml2
-rw-r--r--app/views/projects/runners/_shared_runners.html.haml4
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml5
-rw-r--r--app/views/projects/services/_form.html.haml3
-rw-r--r--app/views/projects/services/prometheus/_configuration_banner.html.haml26
-rw-r--r--app/views/projects/services/prometheus/_help.html.haml28
-rw-r--r--app/views/projects/services/prometheus/_show.html.haml5
-rw-r--r--app/views/projects/settings/repository/show.html.haml3
-rw-r--r--app/views/projects/show.html.haml63
-rw-r--r--app/views/projects/tree/_tree_header.html.haml2
-rw-r--r--app/views/projects/update.js.haml2
-rw-r--r--app/views/search/results/_snippet_blob.html.haml2
-rw-r--r--app/views/search/results/_snippet_title.html.haml2
-rw-r--r--app/views/shared/_label.html.haml2
-rw-r--r--app/views/shared/boards/_show.html.haml2
-rw-r--r--app/views/shared/groups/_dropdown.html.haml6
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml4
-rw-r--r--app/views/shared/members/_member.html.haml4
-rw-r--r--app/views/shared/milestones/_issuable.html.haml4
-rw-r--r--app/views/shared/milestones/_milestone.html.haml2
-rw-r--r--app/views/shared/milestones/_participants_tab.html.haml2
-rw-r--r--app/views/shared/milestones/_sidebar.html.haml23
-rw-r--r--app/views/shared/notes/_note.html.haml2
-rw-r--r--app/views/shared/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml6
-rw-r--r--app/views/shared/snippets/_form.html.haml2
-rw-r--r--app/views/shared/snippets/_snippet.html.haml2
-rw-r--r--app/views/users/show.html.haml84
-rw-r--r--app/workers/all_queues.yml2
-rw-r--r--app/workers/authorized_projects_worker.rb36
-rw-r--r--app/workers/check_gcp_project_billing_worker.rb49
-rw-r--r--app/workers/concerns/waitable_worker.rb44
-rw-r--r--app/workers/pages_domain_verification_cron_worker.rb10
-rw-r--r--app/workers/pages_domain_verification_worker.rb11
-rw-r--r--app/workers/process_commit_worker.rb12
-rw-r--r--app/workers/stuck_import_jobs_worker.rb40
-rwxr-xr-xbin/changelog7
-rw-r--r--changelogs/unreleased-ee/bvl-external-policy-classification.yml5
-rw-r--r--changelogs/unreleased/14256-upload-destroy-removes-file.yml5
-rw-r--r--changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml5
-rw-r--r--changelogs/unreleased/16468-add-fast-blank.yml5
-rw-r--r--changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml5
-rw-r--r--changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml5
-rw-r--r--changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml5
-rw-r--r--changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml5
-rw-r--r--changelogs/unreleased/21554-mark-new-user-as-external.yml5
-rw-r--r--changelogs/unreleased/24035-api-create-application.yml4
-rw-r--r--changelogs/unreleased/24167__color_label.yml5
-rw-r--r--changelogs/unreleased/25327-coverage-badge-rounding.yml5
-rw-r--r--changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml5
-rw-r--r--changelogs/unreleased/26296-update-styling-disabled-buttons.yml5
-rw-r--r--changelogs/unreleased/26388-push-to-create-a-new-project.yml5
-rw-r--r--changelogs/unreleased/26466-natural-sort-mrs.yml4
-rw-r--r--changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml4
-rw-r--r--changelogs/unreleased/28260-fix-pages-custom-domain-url.yml5
-rw-r--r--changelogs/unreleased/29497-pages-custom-domain-dns-verification.yml5
-rw-r--r--changelogs/unreleased/30106-group-issues.yml5
-rw-r--r--changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml5
-rw-r--r--changelogs/unreleased/32282-add-foreign-keys-to-todos.yml5
-rw-r--r--changelogs/unreleased/32283-trending-projects-unique-constraint2.yml5
-rw-r--r--changelogs/unreleased/32564-fix-double-system-closing-notes.yml5
-rw-r--r--changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml6
-rw-r--r--changelogs/unreleased/34252-trailing-plus.yml5
-rw-r--r--changelogs/unreleased/34416-issue-i18n.yml5
-rw-r--r--changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml5
-rw-r--r--changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml5
-rw-r--r--changelogs/unreleased/35418-remove-underline-for-avatar.yml5
-rw-r--r--changelogs/unreleased/35530-teleporting-emoji.yml5
-rw-r--r--changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml5
-rw-r--r--changelogs/unreleased/35856-implement-file-locking-api.yml5
-rw-r--r--changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml5
-rw-r--r--changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml5
-rw-r--r--changelogs/unreleased/37050-ext-issue-tracker.yml5
-rw-r--r--changelogs/unreleased/37199-labels-fix.yml5
-rw-r--r--changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml5
-rw-r--r--changelogs/unreleased/38068-commits-count.yml5
-rw-r--r--changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml5
-rw-r--r--changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml5
-rw-r--r--changelogs/unreleased/38540-ssh-env-file.yml6
-rw-r--r--changelogs/unreleased/39214__pipeline_api.yml5
-rw-r--r--changelogs/unreleased/39607-fix-avatar--vertical-align.yml5
-rw-r--r--changelogs/unreleased/39917-revert-this-merge-request-text.yml5
-rw-r--r--changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml5
-rw-r--r--changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml5
-rw-r--r--changelogs/unreleased/40028-special-characters-on-issuable-templates.yml5
-rw-r--r--changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml5
-rw-r--r--changelogs/unreleased/4020-rebase-message.yml5
-rw-r--r--changelogs/unreleased/40492-update-admin-dashboard-content-order.yml5
-rw-r--r--changelogs/unreleased/40540-use-limit-for-global-search.yml5
-rw-r--r--changelogs/unreleased/40623-fix-404-when-listing-archived-projects-in-a-group-where-all-projects-have-been-archived.yml4
-rw-r--r--changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml5
-rw-r--r--changelogs/unreleased/40755-snippets-author-n-1.yml5
-rw-r--r--changelogs/unreleased/40793-fix-mr-title-for-jira.yml5
-rw-r--r--changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml5
-rw-r--r--changelogs/unreleased/40994-expose-features-as-ci-cd-variable.yml5
-rw-r--r--changelogs/unreleased/41118-add-sorting-to-deployments-api.yml5
-rw-r--r--changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml5
-rw-r--r--changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml5
-rw-r--r--changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml5
-rw-r--r--changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml5
-rw-r--r--changelogs/unreleased/41247-timestamp.yml6
-rw-r--r--changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml5
-rw-r--r--changelogs/unreleased/41532-email-reason.yml5
-rw-r--r--changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml5
-rw-r--r--changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml5
-rw-r--r--changelogs/unreleased/41613-fix-redundant-modal.yml5
-rw-r--r--changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml5
-rw-r--r--changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml6
-rw-r--r--changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml5
-rw-r--r--changelogs/unreleased/41673-blank-query-members-api.yml5
-rw-r--r--changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml5
-rw-r--r--changelogs/unreleased/41731-predicate-memoization.yml5
-rw-r--r--changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml5
-rw-r--r--changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml5
-rw-r--r--changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml5
-rw-r--r--changelogs/unreleased/41763-search-api.yml5
-rw-r--r--changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml5
-rw-r--r--changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml5
-rw-r--r--changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml6
-rw-r--r--changelogs/unreleased/41814-text-decoration-skip.yml5
-rw-r--r--changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml5
-rw-r--r--changelogs/unreleased/41949-move.yml5
-rw-r--r--changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml5
-rw-r--r--changelogs/unreleased/42044-osw-add-button-to-deploy-runner-to-kubernetes.yml5
-rw-r--r--changelogs/unreleased/42047-pg-10-support.yml5
-rw-r--r--changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml5
-rw-r--r--changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml5
-rw-r--r--changelogs/unreleased/42154-fix-artifact-size-calc.yml5
-rw-r--r--changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml5
-rw-r--r--changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml5
-rw-r--r--changelogs/unreleased/42206-permit-password-for-git-param.yml5
-rw-r--r--changelogs/unreleased/42220-add-pending-empty-state.yml5
-rw-r--r--changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml5
-rw-r--r--changelogs/unreleased/42251-explicit-timezone-for-karma.yml5
-rw-r--r--changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml6
-rw-r--r--changelogs/unreleased/42274-group-request-membership-long-too.yml5
-rw-r--r--changelogs/unreleased/42285-not-found-status-icon.yml5
-rw-r--r--changelogs/unreleased/42314-diff-file.yml5
-rw-r--r--changelogs/unreleased/42332-actionview-template-error-366-524-out-of-range.yml5
-rw-r--r--changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml6
-rw-r--r--changelogs/unreleased/42462-edit-note.yml5
-rw-r--r--changelogs/unreleased/42481-remove-notification-settings-left-projects.yml5
-rw-r--r--changelogs/unreleased/42497-rubocop-style-regexpliteral.yml5
-rw-r--r--changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml5
-rw-r--r--changelogs/unreleased/42545-milestion-quick-actions-for-groups.yml5
-rw-r--r--changelogs/unreleased/42547-upload-store-mount-point.yml5
-rw-r--r--changelogs/unreleased/42584-fix-margins-in-tag-list.yml5
-rw-r--r--changelogs/unreleased/42591-update-nokogiri.yml5
-rw-r--r--changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml5
-rw-r--r--changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml5
-rw-r--r--changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml5
-rw-r--r--changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml5
-rw-r--r--changelogs/unreleased/42730-close-rugged-repository.yml5
-rw-r--r--changelogs/unreleased/42800-change-usage-of-avatar_icon.yml6
-rw-r--r--changelogs/unreleased/42922-environment-name.yml5
-rw-r--r--changelogs/unreleased/42923-close-issue.yml5
-rw-r--r--changelogs/unreleased/42929-hide-new-variable-values.yml5
-rw-r--r--changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml5
-rw-r--r--changelogs/unreleased/43198-fix-settings-panel-expanding-when-fragment-hash-linked.yml5
-rw-r--r--changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml5
-rw-r--r--changelogs/unreleased/43261-fix-import-from-url-name-collision-active-tab.yml6
-rw-r--r--changelogs/unreleased/43261-fix-prometheus-installation.yml5
-rw-r--r--changelogs/unreleased/43275-improve-variables-validation-message.yml5
-rw-r--r--changelogs/unreleased/43315-gpg-popover.yml5
-rw-r--r--changelogs/unreleased/43496-error-message-for-gke-clusters-persists-in-the-next-page.yml5
-rw-r--r--changelogs/unreleased/43510-merge-requests-and-issues-don-t-show-for-all-subgroups.yml6
-rw-r--r--changelogs/unreleased/43532-error-on-admin-applications-prometheus-template.yml5
-rw-r--r--changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml5
-rw-r--r--changelogs/unreleased/4826-geo-wikisyncservice-attempts-to-sync-projects.yml5
-rw-r--r--changelogs/unreleased/4826-github-import-wiki-fix-1.yml5
-rw-r--r--changelogs/unreleased/add-confirmation-input-for-modals.yml5
-rw-r--r--changelogs/unreleased/api-refs-for-commit.yml5
-rw-r--r--changelogs/unreleased/asciidoc_inter_document_cross_references.yml5
-rw-r--r--changelogs/unreleased/bump-workhorse.yml5
-rw-r--r--changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml5
-rw-r--r--changelogs/unreleased/change-strip-whitespace-from-username-input-42637.yml5
-rw-r--r--changelogs/unreleased/contribution_calendar_label_cut_off.yml5
-rw-r--r--changelogs/unreleased/cs-fix-commercial-content-check.yml6
-rw-r--r--changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml5
-rw-r--r--changelogs/unreleased/default-to-https-for-gravatar-urls.yml5
-rw-r--r--changelogs/unreleased/disable-throwOnError-in-katex.yml5
-rw-r--r--changelogs/unreleased/display-mr-in-commit-page.yml5
-rw-r--r--changelogs/unreleased/dm-dont-cache-nil-root-ref.yml5
-rw-r--r--changelogs/unreleased/dm-escape-commit-message.yml5
-rw-r--r--changelogs/unreleased/dm-go-get-api-token.yml (renamed from changelogs/unreleased/36571-ignore-root-in-repo.yml)6
-rw-r--r--changelogs/unreleased/dm-project-system-hooks-in-transaction.yml5
-rw-r--r--changelogs/unreleased/dm-route-path-validation.yml5
-rw-r--r--changelogs/unreleased/dm-stuck-import-jobs-verify.yml5
-rw-r--r--changelogs/unreleased/dm-user-namespace-route-path-validation.yml5
-rw-r--r--changelogs/unreleased/docs-update-vue-naming-guidelines.yml5
-rw-r--r--changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml5
-rw-r--r--changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml5
-rw-r--r--changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml5
-rw-r--r--changelogs/unreleased/feature-26598-clear-button-ci-lint.yml4
-rw-r--r--changelogs/unreleased/feature-39591-visibility-level.yml5
-rw-r--r--changelogs/unreleased/feature-include-custom-attributes-in-api.yml5
-rw-r--r--changelogs/unreleased/feature-merge-request-system-hook.yml5
-rw-r--r--changelogs/unreleased/feature-oidc-groups-claim.yml4
-rw-r--r--changelogs/unreleased/feature-sm-artifacts-trace.yml5
-rw-r--r--changelogs/unreleased/file-content-large-screen-padding.yml5
-rw-r--r--changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml5
-rw-r--r--changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml5
-rw-r--r--changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml5
-rw-r--r--changelogs/unreleased/fix-change-event-body-label-font-size.yml5
-rw-r--r--changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml5
-rw-r--r--changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml5
-rw-r--r--changelogs/unreleased/fix-improve-issue-note-dropdown.yml5
-rw-r--r--changelogs/unreleased/fix-install-docs.yml5
-rw-r--r--changelogs/unreleased/fix-new-project-path-input-overlapping.yml5
-rw-r--r--changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml5
-rw-r--r--changelogs/unreleased/fix-squash-with-renamed-files.yml5
-rw-r--r--changelogs/unreleased/fix-template-project-visibility.yml5
-rw-r--r--changelogs/unreleased/fix_gitlab-ce-41891.yml5
-rw-r--r--changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml5
-rw-r--r--changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml5
-rw-r--r--changelogs/unreleased/fj-42910-unauthenticated-limit-via-ssh.yml5
-rw-r--r--changelogs/unreleased/fl-mr-widget-refactor.yml5
-rw-r--r--changelogs/unreleased/fl-refresh-btn.yml5
-rw-r--r--changelogs/unreleased/gitaly-git-http-ssh.yml6
-rw-r--r--changelogs/unreleased/gitaly-repo-exists.yml5
-rw-r--r--changelogs/unreleased/group-label-page-breadcrumb.yml5
-rw-r--r--changelogs/unreleased/grpc-unavailable-restart.yml5
-rw-r--r--changelogs/unreleased/internationalize-charts-page.yml5
-rw-r--r--changelogs/unreleased/internationalize-graph-page.yml5
-rw-r--r--changelogs/unreleased/issue-39885.yml5
-rw-r--r--changelogs/unreleased/issue-42689-new-file-template.yml5
-rw-r--r--changelogs/unreleased/issue-edit-shortcut.yml5
-rw-r--r--changelogs/unreleased/issue_41460.yml5
-rw-r--r--changelogs/unreleased/issues-closed-at-steal.yml5
-rw-r--r--changelogs/unreleased/jej-upload-file-tracks-lfs.yml5
-rw-r--r--changelogs/unreleased/merge-request-target-branch-perf.yml5
-rw-r--r--changelogs/unreleased/minimal-fix-for-artifacts-service.yml5
-rw-r--r--changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml5
-rw-r--r--changelogs/unreleased/move-board-list-vue-component.yml5
-rw-r--r--changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml5
-rw-r--r--changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml5
-rw-r--r--changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml5
-rw-r--r--changelogs/unreleased/osw-system-notes-for-commits-regression.yml5
-rw-r--r--changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml5
-rw-r--r--changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml6
-rw-r--r--changelogs/unreleased/persistent-callouts.yml5
-rw-r--r--changelogs/unreleased/query-counts.yml5
-rw-r--r--changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml5
-rw-r--r--changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml5
-rw-r--r--changelogs/unreleased/refactor-move-filtered-search-vue-component.yml5
-rw-r--r--changelogs/unreleased/refactor-move-issuable-time-tracker-vue-component.yml5
-rw-r--r--changelogs/unreleased/remove-unnecessary-validate-project.yml5
-rw-r--r--changelogs/unreleased/sh-add-gitaly-health-check.yml5
-rw-r--r--changelogs/unreleased/sh-fix-award-emoji-move-issues.yml5
-rw-r--r--changelogs/unreleased/sh-fix-events-collection.yml5
-rw-r--r--changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml5
-rw-r--r--changelogs/unreleased/sh-fix-jira-trailing-slash.yml5
-rw-r--r--changelogs/unreleased/sh-fix-project-members-api-perf.yml6
-rw-r--r--changelogs/unreleased/sh-log-when-user-blocked.yml5
-rw-r--r--changelogs/unreleased/sh-remove-shared-runners-and-more.yml5
-rw-r--r--changelogs/unreleased/sh-store-user-in-api-logs.yml5
-rw-r--r--changelogs/unreleased/style-include-branch-in-mobile-view.yml5
-rw-r--r--changelogs/unreleased/update-node-docs.yml5
-rw-r--r--changelogs/unreleased/ux-guide-deprecation.yml6
-rw-r--r--changelogs/unreleased/winh-delete-milestone-modal.yml5
-rw-r--r--changelogs/unreleased/winh-kubernetes-clusters.yml5
-rw-r--r--changelogs/unreleased/winh-new-modal-component.yml5
-rw-r--r--changelogs/unreleased/winh-search-page-filters.yml5
-rw-r--r--changelogs/unreleased/winh-style-modals.yml5
-rw-r--r--changelogs/unreleased/zj-gitaly-server-info.yml5
-rw-r--r--changelogs/unreleased/zj-protobuf.yml5
-rw-r--r--config/application.rb5
-rw-r--r--config/dependency_decisions.yml22
-rw-r--r--config/gitlab.yml.example4
-rw-r--r--config/initializers/0_as_concern.rb25
-rw-r--r--config/initializers/1_settings.rb6
-rw-r--r--config/initializers/doorkeeper_openid_connect.rb1
-rw-r--r--config/initializers/flipper.rb2
-rw-r--r--config/initializers/rack_attack_global.rb5
-rw-r--r--config/initializers/sidekiq.rb2
-rw-r--r--config/karma.config.js2
-rw-r--r--config/locales/doorkeeper.en.yml2
-rw-r--r--config/routes/project.rb10
-rw-r--r--config/sidekiq_queues.yml1
-rw-r--r--config/webpack.config.js96
-rw-r--r--db/fixtures/development/01_admin.rb1
-rw-r--r--db/migrate/20170929131201_populate_fork_networks.rb16
-rw-r--r--db/migrate/20180119160751_optimize_ci_job_artifacts.rb23
-rw-r--r--db/migrate/20180208183958_schedule_populate_untracked_uploads_if_needed.rb47
-rw-r--r--db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb21
-rw-r--r--db/migrate/20180215181245_users_name_lower_index.rb29
-rw-r--r--db/migrate/20180216120000_add_pages_domain_verification.rb8
-rw-r--r--db/migrate/20180216120010_add_pages_domain_verified_at_index.rb15
-rw-r--r--db/migrate/20180216120020_allow_domain_verification_to_be_disabled.rb7
-rw-r--r--db/migrate/20180216120030_add_pages_domain_enabled_until.rb7
-rw-r--r--db/migrate/20180216120040_add_pages_domain_enabled_until_index.rb17
-rw-r--r--db/migrate/20180216120050_pages_domains_verification_grace_period.rb26
-rw-r--r--db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb2
-rw-r--r--db/post_migrate/20171124150326_reschedule_fork_network_creation.rb16
-rw-r--r--db/post_migrate/20180216121020_fill_pages_domain_verification_code.rb41
-rw-r--r--db/post_migrate/20180216121030_enqueue_verify_pages_domain_workers.rb16
-rw-r--r--db/schema.rb12
-rw-r--r--doc/README.md10
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md13
-rw-r--r--doc/administration/custom_hooks.md5
-rw-r--r--doc/administration/gitaly/index.md24
-rw-r--r--doc/administration/high_availability/gitlab.md10
-rw-r--r--doc/administration/index.md12
-rw-r--r--doc/administration/integration/plantuml.md14
-rw-r--r--doc/administration/logs.md4
-rw-r--r--doc/administration/monitoring/index.md2
-rw-r--r--doc/administration/operations/fast_ssh_key_lookup.md4
-rw-r--r--doc/administration/pages/index.md27
-rw-r--r--doc/administration/repository_storage_types.md91
-rw-r--r--doc/api/README.md2
-rw-r--r--doc/api/commits.md36
-rw-r--r--doc/api/groups.md4
-rw-r--r--doc/api/namespaces.md10
-rw-r--r--doc/api/project_import_export.md74
-rw-r--r--doc/api/projects.md10
-rw-r--r--doc/api/search.md9
-rw-r--r--doc/api/users.md12
-rw-r--r--doc/ci/README.md2
-rw-r--r--doc/ci/examples/README.md2
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md10
-rw-r--r--doc/ci/examples/browser_performance.md18
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md10
-rw-r--r--doc/ci/examples/test-phoenix-application.md58
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.pngbin0 -> 62101 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.pngbin0 -> 16112 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.pngbin0 -> 73726 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.pngbin0 -> 40381 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.pngbin0 -> 29968 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.pngbin0 -> 46405 bytes
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md423
-rw-r--r--doc/ci/yaml/README.md10
-rw-r--r--doc/customization/branded_login_page.md2
-rw-r--r--doc/development/architecture.md6
-rw-r--r--doc/development/background_migrations.md12
-rw-r--r--doc/development/changelog.md6
-rw-r--r--doc/development/doc_styleguide.md14
-rw-r--r--doc/development/ee_features.md7
-rw-r--r--doc/development/fe_guide/components.md61
-rw-r--r--doc/development/fe_guide/dropdowns.md33
-rw-r--r--doc/development/fe_guide/img/gl-modal.pngbin0 -> 25893 bytes
-rw-r--r--doc/development/fe_guide/index.md9
-rw-r--r--doc/development/fe_guide/style_guide_js.md43
-rw-r--r--doc/development/fe_guide/style_guide_scss.md2
-rw-r--r--doc/development/feature_flags.md7
-rw-r--r--doc/development/i18n/externalization.md3
-rw-r--r--doc/development/i18n/index.md37
-rw-r--r--doc/development/i18n/proofreader.md67
-rw-r--r--doc/development/licensing.md2
-rw-r--r--doc/development/profiling.md11
-rw-r--r--doc/development/query_count_limits.md5
-rw-r--r--doc/development/sidekiq_style_guide.md3
-rw-r--r--doc/development/testing_guide/testing_levels.md4
-rw-r--r--doc/development/writing_documentation.md29
-rw-r--r--doc/gitlab-basics/create-project.md6
-rw-r--r--doc/install/azure/index.md7
-rw-r--r--doc/install/database_mysql.md1
-rw-r--r--doc/install/google_cloud_platform/img/boot_disk.pngbin0 -> 124175 bytes
-rw-r--r--doc/install/google_cloud_platform/img/change_admin_passwd_email.pngbin7193 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/chrome_not_secure_page.pngbin21705 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/first_signin.pngbin0 -> 69516 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.pngbin23486 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_gitlab_overview.pngbin42028 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_landing.pngbin59912 -> 15025 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.pngbin42090 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gcp_search_for_gitlab.pngbin7648 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gitlab_deployed_page.pngbin35573 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gitlab_first_sign_in.pngbin20054 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/gitlab_launch_button.pngbin5198 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/launch_vm.pngbin0 -> 47201 bytes
-rw-r--r--doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.pngbin50014 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/ssh_terminal.pngbin0 -> 94161 bytes
-rw-r--r--doc/install/google_cloud_platform/img/ssh_via_button.pngbin3062 -> 0 bytes
-rw-r--r--doc/install/google_cloud_platform/img/vm_created.pngbin0 -> 13809 bytes
-rw-r--r--doc/install/google_cloud_platform/img/vm_details.pngbin0 -> 76519 bytes
-rw-r--r--doc/install/google_cloud_platform/index.md86
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/install/openshift_and_gitlab/index.md13
-rw-r--r--doc/install/requirements.md3
-rw-r--r--doc/integration/openid_connect_provider.md1
-rw-r--r--doc/topics/autodevops/index.md4
-rw-r--r--doc/topics/git/how_to_install_git/index.md13
-rw-r--r--doc/topics/git/numerous_undo_possibilities_in_git/index.md13
-rw-r--r--doc/update/10.4-to-10.5.md361
-rw-r--r--doc/user/markdown.md8
-rw-r--r--doc/user/project/clusters/index.md117
-rw-r--r--doc/user/project/img/label_priority_sort_order.pngbin101667 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_assign_label_sidebar.pngbin11767 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_assign_label_sidebar_saved.pngbin9741 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_default.pngbin24404 -> 22975 bytes
-rw-r--r--doc/user/project/img/labels_description_tooltip.pngbin8538 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_filter.pngbin19071 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_generate.pngbin13628 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_generate_default.pngbin0 -> 65549 bytes
-rw-r--r--doc/user/project/img/labels_group_issues.pngbin0 -> 264573 bytes
-rw-r--r--doc/user/project/img/labels_list.pngbin0 -> 207736 bytes
-rw-r--r--doc/user/project/img/labels_new_label.pngbin10720 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_new_label_on_the_fly.pngbin4625 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_new_label_on_the_fly_create.pngbin6389 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_prioritize.pngbin24194 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_prioritized.pngbin0 -> 156020 bytes
-rw-r--r--doc/user/project/img/labels_promotion.pngbin0 -> 121824 bytes
-rw-r--r--doc/user/project/img/labels_sidebar.pngbin0 -> 31123 bytes
-rw-r--r--doc/user/project/img/labels_sidebar_assign.pngbin0 -> 28033 bytes
-rw-r--r--doc/user/project/img/labels_sidebar_inline.pngbin0 -> 28423 bytes
-rw-r--r--doc/user/project/img/labels_sort_label_priority.pngbin0 -> 110154 bytes
-rw-r--r--doc/user/project/img/labels_sort_priority.pngbin0 -> 108780 bytes
-rw-r--r--doc/user/project/img/labels_subscribe.pngbin5336 -> 0 bytes
-rw-r--r--doc/user/project/img/labels_subscriptions.pngbin0 -> 87502 bytes
-rw-r--r--doc/user/project/img/new_label_from_sidebar.gifbin0 -> 759243 bytes
-rw-r--r--doc/user/project/integrations/bugzilla.md6
-rw-r--r--doc/user/project/integrations/custom_issue_tracker.md2
-rw-r--r--doc/user/project/integrations/img/prometheus_dashboard.pngbin0 -> 26112 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_deploy.pngbin0 -> 27258 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_gcp_firewall_rule.pngbin15247 -> 0 bytes
-rw-r--r--doc/user/project/integrations/img/prometheus_gcp_node_name.pngbin52622 -> 0 bytes
-rw-r--r--doc/user/project/integrations/jira.md2
-rw-r--r--doc/user/project/integrations/prometheus.md128
-rw-r--r--doc/user/project/integrations/prometheus_library/kubernetes.md7
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress.md28
-rw-r--r--doc/user/project/integrations/redmine.md2
-rw-r--r--doc/user/project/integrations/samples/prometheus.yml107
-rw-r--r--doc/user/project/issues/index.md4
-rw-r--r--doc/user/project/issues/issues_functionalities.md4
-rw-r--r--doc/user/project/labels.md189
-rw-r--r--doc/user/project/merge_requests/index.md21
-rw-r--r--doc/user/project/merge_requests/work_in_progress_merge_requests.md3
-rw-r--r--doc/user/project/milestones/img/milestone_create.pngbin40591 -> 0 bytes
-rw-r--r--doc/user/project/milestones/img/milestone_group_create.pngbin35526 -> 0 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_new_group_milestone.pngbin0 -> 156704 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_new_project_milestone.pngbin0 -> 173762 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_project_milestone_page.pngbin0 -> 489382 bytes
-rw-r--r--doc/user/project/milestones/img/milestones_promote_milestone.pngbin0 -> 350399 bytes
-rw-r--r--doc/user/project/milestones/img/sidebar.pngbin89947 -> 0 bytes
-rw-r--r--doc/user/project/milestones/index.md128
-rw-r--r--doc/user/project/pages/getting_started_part_four.md32
-rw-r--r--doc/user/project/pages/getting_started_part_one.md39
-rw-r--r--doc/user/project/pages/getting_started_part_three.md120
-rw-r--r--doc/user/project/pages/getting_started_part_two.md63
-rw-r--r--doc/user/project/pages/img/verify_your_domain.pngbin0 -> 30163 bytes
-rw-r--r--doc/user/project/pages/index.md9
-rw-r--r--doc/user/project/repository/index.md5
-rw-r--r--doc/user/project/settings/import_export.md1
-rw-r--r--doc/workflow/lfs/manage_large_binaries_with_git_lfs.md4
-rw-r--r--features/group/members.feature12
-rw-r--r--features/group/milestones.feature48
-rw-r--r--features/profile/profile.feature85
-rw-r--r--features/project/network_graph.feature46
-rw-r--r--features/project/pages.feature87
-rw-r--r--features/steps/group/members.rb15
-rw-r--r--features/steps/group/milestones.rb135
-rw-r--r--features/steps/profile/profile.rb226
-rw-r--r--features/steps/project/network_graph.rb116
-rw-r--r--features/steps/project/pages.rb152
-rw-r--r--features/support/env.rb7
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/commits.rb28
-rw-r--r--lib/api/entities.rb26
-rw-r--r--lib/api/groups.rb29
-rw-r--r--lib/api/helpers.rb2
-rw-r--r--lib/api/helpers/custom_attributes.rb28
-rw-r--r--lib/api/internal.rb2
-rw-r--r--lib/api/pages_domains.rb10
-rw-r--r--lib/api/project_import.rb69
-rw-r--r--lib/api/projects.rb20
-rw-r--r--lib/api/search.rb12
-rw-r--r--lib/api/settings.rb2
-rw-r--r--lib/api/todos.rb2
-rw-r--r--lib/api/users.rb9
-rw-r--r--lib/api/v3/todos.rb2
-rw-r--r--lib/banzai/filter/html_entity_filter.rb2
-rw-r--r--lib/banzai/filter/issuable_state_filter.rb6
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb34
-rw-r--r--lib/banzai/redactor.rb21
-rw-r--r--lib/banzai/reference_parser/issuable_parser.rb2
-rw-r--r--lib/banzai/reference_parser/issue_parser.rb25
-rw-r--r--lib/gitlab/asciidoc.rb3
-rw-r--r--lib/gitlab/auth/request_authenticator.rb8
-rw-r--r--lib/gitlab/background_migration/create_fork_network_memberships_range.rb15
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads.rb160
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb201
-rw-r--r--lib/gitlab/background_migration/prepare_untracked_uploads.rb15
-rw-r--r--lib/gitlab/checks/change_access.rb14
-rw-r--r--lib/gitlab/checks/commit_check.rb4
-rw-r--r--lib/gitlab/contributions_calendar.rb6
-rw-r--r--lib/gitlab/cross_project_access.rb67
-rw-r--r--lib/gitlab/cross_project_access/check_collection.rb47
-rw-r--r--lib/gitlab/cross_project_access/check_info.rb66
-rw-r--r--lib/gitlab/cross_project_access/class_methods.rb48
-rw-r--r--lib/gitlab/diff/highlight.rb12
-rw-r--r--lib/gitlab/encoding_helper.rb6
-rw-r--r--lib/gitlab/file_finder.rb5
-rw-r--r--lib/gitlab/git/commit.rb2
-rw-r--r--lib/gitlab/git/repository.rb58
-rw-r--r--lib/gitlab/git/tree.rb23
-rw-r--r--lib/gitlab/git_access.rb17
-rw-r--r--lib/gitlab/git_access_wiki.rb8
-rw-r--r--lib/gitlab/gitaly_client.rb23
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb5
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb17
-rw-r--r--lib/gitlab/github_import/importer/repository_importer.rb1
-rw-r--r--lib/gitlab/gon_helper.rb2
-rw-r--r--lib/gitlab/gpg/commit.rb4
-rw-r--r--lib/gitlab/import_export/importer.rb5
-rw-r--r--lib/gitlab/import_export/project_creator.rb23
-rw-r--r--lib/gitlab/import_export/wiki_restorer.rb23
-rw-r--r--lib/gitlab/job_waiter.rb8
-rw-r--r--lib/gitlab/ldap/config.rb2
-rw-r--r--lib/gitlab/ldap/person.rb15
-rw-r--r--lib/gitlab/metrics/sidekiq_metrics_exporter.rb2
-rw-r--r--lib/gitlab/metrics/transaction.rb5
-rw-r--r--lib/gitlab/middleware/go.rb10
-rw-r--r--lib/gitlab/middleware/multipart.rb8
-rw-r--r--lib/gitlab/o_auth/user.rb8
-rw-r--r--lib/gitlab/profiler.rb1
-rw-r--r--lib/gitlab/project_search_results.rb5
-rw-r--r--lib/gitlab/prometheus/metric_group.rb7
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb1
-rw-r--r--lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb1
-rw-r--r--lib/gitlab/prometheus/queries/matched_metrics_query.rb2
-rw-r--r--lib/gitlab/prometheus/queries/query_additional_metrics.rb8
-rw-r--r--lib/gitlab/prometheus_client.rb33
-rw-r--r--lib/gitlab/query_limiting.rb2
-rw-r--r--lib/gitlab/query_limiting/active_support_subscriber.rb6
-rw-r--r--lib/gitlab/query_limiting/transaction.rb8
-rw-r--r--lib/gitlab/quick_actions/command_definition.rb15
-rw-r--r--lib/gitlab/quick_actions/dsl.rb5
-rw-r--r--lib/gitlab/quick_actions/extractor.rb10
-rw-r--r--lib/gitlab/regex.rb8
-rw-r--r--lib/gitlab/search_results.rb3
-rw-r--r--lib/gitlab/setup_helper.rb2
-rw-r--r--lib/gitlab/shell.rb12
-rw-r--r--lib/gitlab/sidekiq_middleware/memory_killer.rb67
-rw-r--r--lib/gitlab/sidekiq_middleware/shutdown.rb133
-rw-r--r--lib/gitlab/slash_commands/base_command.rb7
-rw-r--r--lib/gitlab/slash_commands/command.rb5
-rw-r--r--lib/gitlab/sql/pattern.rb14
-rw-r--r--lib/gitlab/ssh_public_key.rb2
-rw-r--r--lib/gitlab/user_access.rb2
-rw-r--r--lib/google_api/cloud_platform/client.rb6
-rw-r--r--lib/tasks/gemojione.rake2
-rw-r--r--lib/tasks/gitlab/gitaly.rake4
-rw-r--r--lib/tasks/lint.rake49
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake2
-rw-r--r--locale/bg/gitlab.po1203
-rw-r--r--locale/de/gitlab.po1203
-rw-r--r--locale/eo/gitlab.po1201
-rw-r--r--locale/es/gitlab.po1265
-rw-r--r--locale/fr/gitlab.po1389
-rw-r--r--locale/gitlab.pot133
-rw-r--r--locale/it/gitlab.po1267
-rw-r--r--locale/ja/gitlab.po1194
-rw-r--r--locale/ko/gitlab.po1194
-rw-r--r--locale/nl_NL/gitlab.po1197
-rw-r--r--locale/pl_PL/gitlab.po1202
-rw-r--r--locale/pt_BR/gitlab.po1403
-rw-r--r--locale/ru/gitlab.po1562
-rw-r--r--locale/uk/gitlab.po1514
-rw-r--r--locale/zh_CN/gitlab.po1322
-rw-r--r--locale/zh_HK/gitlab.po1194
-rw-r--r--locale/zh_TW/gitlab.po1250
-rw-r--r--package.json36
-rw-r--r--qa/README.md24
-rw-r--r--qa/qa.rb7
-rw-r--r--qa/qa/factory/base.rb2
-rw-r--r--qa/qa/factory/product.rb5
-rw-r--r--qa/qa/factory/repository/push.rb4
-rw-r--r--qa/qa/factory/resource/merge_request.rb9
-rw-r--r--qa/qa/factory/resource/runner.rb7
-rw-r--r--qa/qa/factory/resource/secret_variable.rb12
-rw-r--r--qa/qa/git/location.rb32
-rw-r--r--qa/qa/git/repository.rb19
-rw-r--r--qa/qa/page/base.rb7
-rw-r--r--qa/qa/page/main/login.rb35
-rw-r--r--qa/qa/page/merge_request/show.rb46
-rw-r--r--qa/qa/page/project/job/show.rb19
-rw-r--r--qa/qa/page/project/pipeline/index.rb8
-rw-r--r--qa/qa/page/project/pipeline/show.rb11
-rw-r--r--qa/qa/page/project/settings/merge_request.rb27
-rw-r--r--qa/qa/page/project/settings/secret_variables.rb26
-rw-r--r--qa/qa/page/project/show.rb39
-rw-r--r--qa/qa/runtime/env.rb30
-rw-r--r--qa/qa/runtime/namespace.rb2
-rw-r--r--qa/qa/runtime/rsa_key.rb2
-rw-r--r--qa/qa/runtime/user.rb20
-rw-r--r--qa/qa/service/runner.rb10
-rw-r--r--qa/qa/service/shellout.rb4
-rw-r--r--qa/qa/specs/features/api/users_spec.rb4
-rw-r--r--qa/qa/specs/features/merge_request/rebase_spec.rb39
-rw-r--r--qa/qa/specs/features/project/add_secret_variable_spec.rb21
-rw-r--r--qa/qa/specs/features/project/deploy_key_clone_spec.rb81
-rw-r--r--qa/qa/specs/features/project/pipelines_spec.rb4
-rw-r--r--qa/spec/factory/base_spec.rb17
-rw-r--r--qa/spec/factory/product_spec.rb16
-rw-r--r--qa/spec/git/location_spec.rb55
-rw-r--r--qa/spec/page/base_spec.rb11
-rw-r--r--qa/spec/runtime/env_spec.rb21
-rwxr-xr-xscripts/codequality19
-rwxr-xr-xscripts/security-harness55
-rwxr-xr-xscripts/static-analysis11
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb10
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb1
-rw-r--r--spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb146
-rw-r--r--spec/controllers/concerns/issuable_collections_spec.rb4
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb98
-rw-r--r--spec/controllers/import/gitlab_controller_spec.rb95
-rw-r--r--spec/controllers/profiles_controller_spec.rb40
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests/creations_controller_spec.rb64
-rw-r--r--spec/controllers/projects/pages_domains_controller_spec.rb41
-rw-r--r--spec/controllers/projects/prometheus/metrics_controller_spec.rb (renamed from spec/controllers/projects/prometheus_controller_spec.rb)16
-rw-r--r--spec/controllers/projects/uploads_controller_spec.rb8
-rw-r--r--spec/controllers/projects_controller_spec.rb96
-rw-r--r--spec/controllers/search_controller_spec.rb26
-rw-r--r--spec/controllers/users_controller_spec.rb25
-rw-r--r--spec/factories/ci/builds.rb4
-rw-r--r--spec/factories/keys.rb112
-rw-r--r--spec/factories/pages_domains.rb21
-rw-r--r--spec/factories/projects.rb6
-rw-r--r--spec/features/admin/admin_runners_spec.rb4
-rw-r--r--spec/features/admin/services/admin_activates_prometheus_spec.rb21
-rw-r--r--spec/features/auto_deploy_spec.rb77
-rw-r--r--spec/features/ci_lint_spec.rb34
-rw-r--r--spec/features/groups/empty_states_spec.rb124
-rw-r--r--spec/features/groups/members/search_members_spec.rb29
-rw-r--r--spec/features/groups/milestone_spec.rb151
-rw-r--r--spec/features/issues/filtered_search/recent_searches_spec.rb4
-rw-r--r--spec/features/issues/form_spec.rb12
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb (renamed from spec/features/copy_as_gfm_spec.rb)0
-rw-r--r--spec/features/markdown/gitlab_flavored_markdown_spec.rb (renamed from spec/features/gitlab_flavored_markdown_spec.rb)0
-rw-r--r--spec/features/markdown/markdown_spec.rb (renamed from spec/features/markdown_spec.rb)0
-rw-r--r--spec/features/markdown/math_spec.rb22
-rw-r--r--spec/features/markdown/mermaid_spec.rb24
-rw-r--r--spec/features/milestone_spec.rb15
-rw-r--r--spec/features/profiles/password_spec.rb76
-rw-r--r--spec/features/profiles/user_edit_profile_spec.rb58
-rw-r--r--spec/features/profiles/user_manages_applications_spec.rb39
-rw-r--r--spec/features/profiles/user_visits_profile_authentication_log_spec.rb25
-rw-r--r--spec/features/profiles/user_visits_profile_spec.rb52
-rw-r--r--spec/features/projects/clusters/gcp_spec.rb4
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb2
-rw-r--r--spec/features/projects/import_export/namespace_export_file_spec.rb6
-rw-r--r--spec/features/projects/members/share_with_group_spec.rb5
-rw-r--r--spec/features/projects/network_graph_spec.rb108
-rw-r--r--spec/features/projects/new_project_spec.rb14
-rw-r--r--spec/features/projects/pages_spec.rb202
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb92
-rw-r--r--spec/features/projects/services/user_activates_jira_spec.rb31
-rw-r--r--spec/features/projects/services/user_activates_prometheus_spec.rb23
-rw-r--r--spec/features/projects/show_project_spec.rb317
-rw-r--r--spec/features/projects_spec.rb26
-rw-r--r--spec/features/runners_spec.rb14
-rw-r--r--spec/features/signup_spec.rb103
-rw-r--r--spec/features/tags/master_views_tags_spec.rb2
-rw-r--r--spec/features/users/login_spec.rb (renamed from spec/features/login_spec.rb)20
-rw-r--r--spec/features/users/logout_spec.rb (renamed from spec/features/logout_spec.rb)0
-rw-r--r--spec/features/users/projects_spec.rb29
-rw-r--r--spec/features/users/show_spec.rb17
-rw-r--r--spec/features/users/signup_spec.rb135
-rw-r--r--spec/features/users/user_browses_projects_on_user_page_spec.rb (renamed from spec/features/user_page_spec.rb)24
-rw-r--r--spec/features/users_spec.rb114
-rw-r--r--spec/finders/concerns/finder_methods_spec.rb70
-rw-r--r--spec/finders/concerns/finder_with_cross_project_access_spec.rb118
-rw-r--r--spec/finders/events_finder_spec.rb8
-rw-r--r--spec/finders/labels_finder_spec.rb23
-rw-r--r--spec/finders/milestones_finder_spec.rb8
-rw-r--r--spec/finders/snippets_finder_spec.rb85
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb31
-rw-r--r--spec/fixtures/api/schemas/deployment.json45
-rw-r--r--spec/fixtures/api/schemas/deployments.json44
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/blobs.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/commit/detail.json7
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/commits_details.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json5
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json5
-rw-r--r--spec/fixtures/api/schemas/variable.json3
-rw-r--r--spec/helpers/application_helper_spec.rb46
-rw-r--r--spec/helpers/avatars_helper_spec.rb16
-rw-r--r--spec/helpers/blob_helper_spec.rb8
-rw-r--r--spec/helpers/dashboard_helper_spec.rb24
-rw-r--r--spec/helpers/events_helper_spec.rb4
-rw-r--r--spec/helpers/explore_helper_spec.rb18
-rw-r--r--spec/helpers/groups_helper_spec.rb35
-rw-r--r--spec/helpers/issuables_helper_spec.rb32
-rw-r--r--spec/helpers/issues_helper_spec.rb15
-rw-r--r--spec/helpers/nav_helper_spec.rb53
-rw-r--r--spec/helpers/preferences_helper_spec.rb97
-rw-r--r--spec/helpers/projects_helper_spec.rb54
-rw-r--r--spec/helpers/users_helper_spec.rb13
-rw-r--r--spec/javascripts/ajax_loading_spinner_spec.js2
-rw-r--r--spec/javascripts/awards_handler_spec.js25
-rw-r--r--spec/javascripts/behaviors/requires_input_spec.js2
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js2
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js46
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js45
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js2
-rw-r--r--spec/javascripts/clusters/components/application_row_spec.js2
-rw-r--r--spec/javascripts/clusters/components/applications_spec.js2
-rw-r--r--spec/javascripts/commit/commit_pipeline_status_component_spec.js2
-rw-r--r--spec/javascripts/commits_spec.js20
-rw-r--r--spec/javascripts/cycle_analytics/banner_spec.js2
-rw-r--r--spec/javascripts/cycle_analytics/total_time_component_spec.js2
-rw-r--r--spec/javascripts/droplab/drop_down_spec.js113
-rw-r--r--spec/javascripts/environments/emtpy_state_spec.js2
-rw-r--r--spec/javascripts/environments/environment_table_spec.js2
-rw-r--r--spec/javascripts/environments/environments_app_spec.js4
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js4
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_helper_spec.js2
-rw-r--r--spec/javascripts/feature_highlight/feature_highlight_spec.js35
-rw-r--r--spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js3
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js36
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js95
-rw-r--r--spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js22
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js67
-rw-r--r--spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js22
-rw-r--r--spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js19
-rw-r--r--spec/javascripts/gl_dropdown_spec.js4
-rw-r--r--spec/javascripts/gl_form_spec.js20
-rw-r--r--spec/javascripts/gpg_badges_spec.js50
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_graph_spec.js2
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_spec.js4
-rw-r--r--spec/javascripts/graphs/stat_graph_contributors_util_spec.js2
-rw-r--r--spec/javascripts/groups/components/group_item_spec.js3
-rw-r--r--spec/javascripts/groups/components/groups_spec.js3
-rw-r--r--spec/javascripts/groups/components/item_actions_spec.js3
-rw-r--r--spec/javascripts/groups/components/item_caret_spec.js2
-rw-r--r--spec/javascripts/groups/components/item_stats_spec.js3
-rw-r--r--spec/javascripts/groups/components/item_stats_value_spec.js2
-rw-r--r--spec/javascripts/groups/components/item_type_icon_spec.js3
-rw-r--r--spec/javascripts/importer_status_spec.js107
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js2
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js2
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js2
-rw-r--r--spec/javascripts/jobs/header_spec.js2
-rw-r--r--spec/javascripts/merge_request_notes_spec.js4
-rw-r--r--spec/javascripts/notebook/cells/markdown_spec.js2
-rw-r--r--spec/javascripts/notes/helpers.js12
-rw-r--r--spec/javascripts/notes/mock_data.js2
-rw-r--r--spec/javascripts/notes/stores/actions_spec.js71
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js6
-rw-r--r--spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js6
-rw-r--r--spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js2
-rw-r--r--spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js2
-rw-r--r--spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js (renamed from spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js)2
-rw-r--r--spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js (renamed from spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js)2
-rw-r--r--spec/javascripts/pipelines/async_button_spec.js11
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipeline_details_mediator_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipeline_store_spec.js1
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/profile/account/components/delete_account_modal_spec.js2
-rw-r--r--spec/javascripts/projects/project_import_gitlab_project_spec.js2
-rw-r--r--spec/javascripts/projects/project_new_spec.js4
-rw-r--r--spec/javascripts/projects_dropdown/components/app_spec.js2
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js2
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_item_spec.js2
-rw-r--r--spec/javascripts/projects_dropdown/components/projects_list_search_spec.js2
-rw-r--r--spec/javascripts/projects_dropdown/components/search_spec.js2
-rw-r--r--spec/javascripts/registry/components/app_spec.js2
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js2
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_item_spec.js2
-rw-r--r--spec/javascripts/repo/components/commit_sidebar/list_spec.js2
-rw-r--r--spec/javascripts/repo/components/ide_context_bar_spec.js2
-rw-r--r--spec/javascripts/repo/components/ide_side_bar_spec.js2
-rw-r--r--spec/javascripts/repo/components/ide_spec.js2
-rw-r--r--spec/javascripts/repo/components/new_branch_form_spec.js2
-rw-r--r--spec/javascripts/repo/components/new_dropdown/index_spec.js2
-rw-r--r--spec/javascripts/repo/components/new_dropdown/modal_spec.js2
-rw-r--r--spec/javascripts/repo/components/new_dropdown/upload_spec.js2
-rw-r--r--spec/javascripts/repo/components/repo_commit_section_spec.js2
-rw-r--r--spec/javascripts/settings_panels_spec.js29
-rw-r--r--spec/javascripts/sidebar/lock/edit_form_buttons_spec.js2
-rw-r--r--spec/javascripts/sidebar/participants_spec.js2
-rw-r--r--spec/javascripts/sidebar/sidebar_assignees_spec.js2
-rw-r--r--spec/javascripts/sidebar/sidebar_move_issue_spec.js6
-rw-r--r--spec/javascripts/sidebar/sidebar_subscriptions_spec.js2
-rw-r--r--spec/javascripts/sidebar/subscriptions_spec.js2
-rw-r--r--spec/javascripts/test_bundle.js9
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js2
-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_archived_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_closed_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js2
-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_merge_when_pipeline_succeeds_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js34
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js29
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js27
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js11
-rw-r--r--spec/javascripts/vue_shared/components/ci_badge_link_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/clipboard_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/confirmation_input_spec.js63
-rw-r--r--spec/javascripts/vue_shared/components/expand_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/gl_modal_spec.js192
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/icon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/issue/issue_warning_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/loading_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/modal_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/navigation_tabs_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/panel_resizer_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/pikaday_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/toggle_button_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js2
-rw-r--r--spec/lib/backup/repository_spec.rb18
-rw-r--r--spec/lib/banzai/commit_renderer_spec.rb2
-rw-r--r--spec/lib/banzai/filter/html_entity_filter_spec.rb9
-rw-r--r--spec/lib/banzai/filter/issuable_state_filter_spec.rb8
-rw-r--r--spec/lib/banzai/filter/redactor_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb57
-rw-r--r--spec/lib/banzai/redactor_spec.rb51
-rw-r--r--spec/lib/banzai/reference_parser/issue_parser_spec.rb47
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb8
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb262
-rw-r--r--spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb418
-rw-r--r--spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb215
-rw-r--r--spec/lib/gitlab/bare_repository_import/importer_spec.rb6
-rw-r--r--spec/lib/gitlab/checks/change_access_spec.rb4
-rw-r--r--spec/lib/gitlab/closing_issue_extractor_spec.rb14
-rw-r--r--spec/lib/gitlab/contributions_calendar_spec.rb13
-rw-r--r--spec/lib/gitlab/cross_project_access/check_collection_spec.rb55
-rw-r--r--spec/lib/gitlab/cross_project_access/check_info_spec.rb111
-rw-r--r--spec/lib/gitlab/cross_project_access/class_methods_spec.rb46
-rw-r--r--spec/lib/gitlab/cross_project_access_spec.rb84
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb14
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb11
-rw-r--r--spec/lib/gitlab/diff/highlight_spec.rb22
-rw-r--r--spec/lib/gitlab/email/attachment_uploader_spec.rb2
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb5
-rw-r--r--spec/lib/gitlab/gfm/uploads_rewriter_spec.rb4
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb1
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb4
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb84
-rw-r--r--spec/lib/gitlab/git_access_spec.rb32
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb14
-rw-r--r--spec/lib/gitlab/gitaly_client/commit_service_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/repository_service_spec.rb26
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb8
-rw-r--r--spec/lib/gitlab/gpg/commit_spec.rb26
-rw-r--r--spec/lib/gitlab/import_export/uploads_restorer_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/wiki_restorer_spec.rb45
-rw-r--r--spec/lib/gitlab/ldap/config_spec.rb8
-rw-r--r--spec/lib/gitlab/ldap/person_spec.rb9
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb91
-rw-r--r--spec/lib/gitlab/middleware/multipart_spec.rb10
-rw-r--r--spec/lib/gitlab/o_auth/user_spec.rb4
-rw-r--r--spec/lib/gitlab/profiler_spec.rb9
-rw-r--r--spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb6
-rw-r--r--spec/lib/gitlab/prometheus_client_spec.rb28
-rw-r--r--spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb31
-rw-r--r--spec/lib/gitlab/query_limiting/transaction_spec.rb12
-rw-r--r--spec/lib/gitlab/query_limiting_spec.rb10
-rw-r--r--spec/lib/gitlab/quick_actions/command_definition_spec.rb26
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb2
-rw-r--r--spec/lib/gitlab/regex_spec.rb5
-rw-r--r--spec/lib/gitlab/repo_path_spec.rb4
-rw-r--r--spec/lib/gitlab/shell_spec.rb2
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb63
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb88
-rw-r--r--spec/lib/gitlab/slash_commands/command_spec.rb5
-rw-r--r--spec/lib/gitlab/slash_commands/deploy_spec.rb3
-rw-r--r--spec/lib/gitlab/slash_commands/issue_new_spec.rb3
-rw-r--r--spec/lib/gitlab/slash_commands/issue_search_spec.rb3
-rw-r--r--spec/lib/gitlab/slash_commands/issue_show_spec.rb3
-rw-r--r--spec/lib/gitlab/sql/pattern_spec.rb6
-rw-r--r--spec/lib/gitlab/ssh_public_key_spec.rb26
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb2
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb3
-rw-r--r--spec/mailers/emails/pages_domains_spec.rb71
-rw-r--r--spec/mailers/notify_spec.rb70
-rw-r--r--spec/migrations/README.md2
-rw-r--r--spec/migrations/enqueue_verify_pages_domain_workers_spec.rb23
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb2
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb12
-rw-r--r--spec/migrations/track_untracked_uploads_spec.rb2
-rw-r--r--spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb2
-rw-r--r--spec/models/ability_spec.rb95
-rw-r--r--spec/models/chat_name_spec.rb20
-rw-r--r--spec/models/ci/build_spec.rb11
-rw-r--r--spec/models/ci/group_variable_spec.rb2
-rw-r--r--spec/models/ci/variable_spec.rb2
-rw-r--r--spec/models/concerns/protected_ref_access_spec.rb31
-rw-r--r--spec/models/group_spec.rb8
-rw-r--r--spec/models/identity_spec.rb33
-rw-r--r--spec/models/issue_spec.rb50
-rw-r--r--spec/models/key_spec.rb21
-rw-r--r--spec/models/namespace_spec.rb206
-rw-r--r--spec/models/notification_recipient_spec.rb16
-rw-r--r--spec/models/pages_domain_spec.rb144
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb8
-rw-r--r--spec/models/project_spec.rb46
-rw-r--r--spec/models/repository_spec.rb12
-rw-r--r--spec/models/user_spec.rb41
-rw-r--r--spec/policies/issuable_policy_spec.rb8
-rw-r--r--spec/policies/issue_policy_spec.rb88
-rw-r--r--spec/policies/personal_snippet_policy_spec.rb1
-rw-r--r--spec/policies/project_snippet_policy_spec.rb1
-rw-r--r--spec/presenters/project_presenter_spec.rb397
-rw-r--r--spec/requests/api/commits_spec.rb67
-rw-r--r--spec/requests/api/internal_spec.rb68
-rw-r--r--spec/requests/api/issues_spec.rb32
-rw-r--r--spec/requests/api/pages_domains_spec.rb13
-rw-r--r--spec/requests/api/project_import_spec.rb102
-rw-r--r--spec/requests/api/projects_spec.rb15
-rw-r--r--spec/requests/api/search_spec.rb22
-rw-r--r--spec/requests/api/snippets_spec.rb21
-rw-r--r--spec/requests/api/todos_spec.rb6
-rw-r--r--spec/requests/api/v3/issues_spec.rb32
-rw-r--r--spec/requests/api/v3/projects_spec.rb6
-rw-r--r--spec/requests/api/v3/todos_spec.rb6
-rw-r--r--spec/requests/git_http_spec.rb16
-rw-r--r--spec/requests/openid_connect_spec.rb27
-rw-r--r--spec/requests/rack_attack_global_spec.rb10
-rw-r--r--spec/routing/routing_spec.rb16
-rw-r--r--spec/services/chat_names/find_user_service_spec.rb25
-rw-r--r--spec/services/ci/create_trace_artifact_service_spec.rb46
-rw-r--r--spec/services/groups/destroy_service_spec.rb6
-rw-r--r--spec/services/issues/fetch_referenced_merge_requests_service_spec.rb35
-rw-r--r--spec/services/issues/move_service_spec.rb22
-rw-r--r--spec/services/labels/find_or_create_service_spec.rb78
-rw-r--r--spec/services/members/authorized_destroy_service_spec.rb56
-rw-r--r--spec/services/merge_requests/build_service_spec.rb162
-rw-r--r--spec/services/merge_requests/create_from_issue_service_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb161
-rw-r--r--spec/services/projects/create_from_template_service_spec.rb8
-rw-r--r--spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage_migration_service_spec.rb2
-rw-r--r--spec/services/projects/transfer_service_spec.rb4
-rw-r--r--spec/services/projects/update_service_spec.rb2
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb16
-rw-r--r--spec/services/search/snippet_service_spec.rb2
-rw-r--r--spec/services/todo_service_spec.rb3
-rw-r--r--spec/services/users/destroy_service_spec.rb4
-rw-r--r--spec/services/verify_pages_domain_service_spec.rb270
-rw-r--r--spec/spec_helper.rb24
-rw-r--r--spec/support/capybara.rb6
-rw-r--r--spec/support/controllers/githubish_import_controller_shared_examples.rb152
-rw-r--r--spec/support/db_cleaner.rb4
-rw-r--r--spec/support/factory_bot.rb (renamed from spec/support/factory_girl.rb)0
-rw-r--r--spec/support/features/variable_list_shared_examples.rb12
-rw-r--r--spec/support/fixture_helpers.rb8
-rw-r--r--spec/support/migrations_helpers.rb19
-rw-r--r--spec/support/migrations_helpers/track_untracked_uploads_helpers.rb128
-rw-r--r--spec/support/prometheus/additional_metrics_shared_examples.rb16
-rw-r--r--spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb13
-rw-r--r--spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb158
-rw-r--r--spec/support/snippet_visibility.rb322
-rw-r--r--spec/support/test_env.rb2
-rw-r--r--spec/support/track_untracked_uploads_helpers.rb20
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb2
-rw-r--r--spec/uploaders/file_uploader_spec.rb6
-rw-r--r--spec/validators/variable_duplicates_validator_spec.rb67
-rw-r--r--spec/views/shared/projects/_project.html.haml_spec.rb1
-rw-r--r--spec/workers/authorized_projects_worker_spec.rb79
-rw-r--r--spec/workers/check_gcp_project_billing_worker_spec.rb65
-rw-r--r--spec/workers/concerns/waitable_worker_spec.rb92
-rw-r--r--spec/workers/pages_domain_verification_cron_worker_spec.rb21
-rw-r--r--spec/workers/pages_domain_verification_worker_spec.rb27
-rw-r--r--spec/workers/process_commit_worker_spec.rb49
-rw-r--r--spec/workers/repository_fork_worker_spec.rb2
-rw-r--r--spec/workers/storage_migrator_worker_spec.rb2
-rw-r--r--spec/workers/stuck_import_jobs_worker_spec.rb58
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.eotbin71656 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.ttfbin71428 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.woffbin40200 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_AMS-Regular.woff2bin33188 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.eotbin19836 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttfbin19588 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.woffbin12136 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2bin10604 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.eotbin19220 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttfbin18960 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.woffbin11868 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2bin10396 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.eotbin36200 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.ttfbin35968 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.woffbin23388 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2bin20476 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.eotbin34896 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.ttfbin34652 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.woffbin22844 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2bin19868 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.eotbin60688 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.ttfbin60468 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.woffbin35480 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Bold.woff2bin29492 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.eotbin44132 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.ttfbin43904 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.woffbin24880 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Italic.woff2bin21032 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.eotbin68228 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.ttfbin67996 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.woffbin37620 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Main-Regular.woff2bin31220 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.eotbin39990 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.ttfbin39744 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.woffbin23192 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2bin20036 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.eotbin41676 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.ttfbin41448 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.woffbin23820 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Italic.woff2bin20432 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.eotbin41536 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.ttfbin41304 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.woffbin23712 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Math-Regular.woff2bin20344 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.eotbin34204 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.ttfbin33964 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.woffbin19196 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2bin16020 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.eotbin31320 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.ttfbin31072 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.woffbin18080 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2bin15152 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.eotbin30212 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.ttfbin29960 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.woffbin16744 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2bin13908 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.eotbin25104 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.ttfbin24864 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.woffbin13856 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Script-Regular.woff2bin12276 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.eotbin13408 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.ttfbin13172 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.woffbin6980 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size1-Regular.woff2bin5820 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.eotbin12648 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.ttfbin12412 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.woffbin6684 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size2-Regular.woff2bin5560 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.eotbin8596 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.ttfbin8360 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.woffbin4776 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size3-Regular.woff2bin3856 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.eotbin11520 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.ttfbin11284 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.woffbin6456 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Size4-Regular.woff2bin5172 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.eotbin35784 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.ttfbin35528 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.woffbin20712 -> 0 bytes
-rw-r--r--vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2bin17344 -> 0 bytes
-rw-r--r--vendor/assets/javascripts/Chart.js3477
-rw-r--r--vendor/assets/javascripts/jasmine-jquery.js851
-rw-r--r--vendor/assets/javascripts/jquery.atwho.js4
-rw-r--r--vendor/assets/javascripts/jquery.endless-scroll.js2
-rw-r--r--vendor/assets/javascripts/jquery.waitforimages.js144
-rw-r--r--vendor/assets/javascripts/katex.js8685
-rw-r--r--vendor/assets/javascripts/peek.js2
-rw-r--r--vendor/assets/stylesheets/katex.scss977
-rw-r--r--vendor/gitignore/Android.gitignore7
-rw-r--r--vendor/gitignore/Dart.gitignore1
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore3
-rw-r--r--vendor/gitignore/Python.gitignore1
-rw-r--r--vendor/gitignore/ROS.gitignore2
-rw-r--r--vendor/gitignore/TeX.gitignore1
-rw-r--r--vendor/gitignore/VisualStudio.gitignore1
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml40
-rw-r--r--vendor/ingress/values.yaml3
-rw-r--r--vendor/licenses.csv1268
-rw-r--r--vendor/prometheus/values.yaml3
-rw-r--r--yarn.lock2850
1664 files changed, 40313 insertions, 27061 deletions
diff --git a/.codeclimate.yml b/.codeclimate.yml
index ecac24b68d7..b02fe54a4ff 100644
--- a/.codeclimate.yml
+++ b/.codeclimate.yml
@@ -15,7 +15,7 @@ engines:
enabled: false
rubocop:
enabled: true
- channel: "gitlab-rubocop-0-52"
+ channel: "gitlab-rubocop-0-52-1"
ratings:
paths:
- Gemfile.lock
diff --git a/.eslintrc b/.eslintrc
index ad5eaebccae..8f9cdfb14ac 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -36,7 +36,7 @@
"import/no-commonjs": "error",
"no-multiple-empty-lines": ["error", { "max": 1 }],
"promise/catch-or-return": "error",
- "no-underscore-dangle": ["error", { "allow": ["__"]}],
+ "no-underscore-dangle": ["error", { "allow": ["__", "_links"]}],
"vue/html-self-closing": ["error", {
"html": {
"void": "always",
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9c3556f5cce..ae762e7aa6e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -323,69 +323,69 @@ setup-test-env:
- tmp/tests
- config/secrets.yml
-rspec-pg 0 27: *rspec-metadata-pg
-rspec-pg 1 27: *rspec-metadata-pg
-rspec-pg 2 27: *rspec-metadata-pg
-rspec-pg 3 27: *rspec-metadata-pg
-rspec-pg 4 27: *rspec-metadata-pg
-rspec-pg 5 27: *rspec-metadata-pg
-rspec-pg 6 27: *rspec-metadata-pg
-rspec-pg 7 27: *rspec-metadata-pg
-rspec-pg 8 27: *rspec-metadata-pg
-rspec-pg 9 27: *rspec-metadata-pg
-rspec-pg 10 27: *rspec-metadata-pg
-rspec-pg 11 27: *rspec-metadata-pg
-rspec-pg 12 27: *rspec-metadata-pg
-rspec-pg 13 27: *rspec-metadata-pg
-rspec-pg 14 27: *rspec-metadata-pg
-rspec-pg 15 27: *rspec-metadata-pg
-rspec-pg 16 27: *rspec-metadata-pg
-rspec-pg 17 27: *rspec-metadata-pg
-rspec-pg 18 27: *rspec-metadata-pg
-rspec-pg 19 27: *rspec-metadata-pg
-rspec-pg 20 27: *rspec-metadata-pg
-rspec-pg 21 27: *rspec-metadata-pg
-rspec-pg 22 27: *rspec-metadata-pg
-rspec-pg 23 27: *rspec-metadata-pg
-rspec-pg 24 27: *rspec-metadata-pg
-rspec-pg 25 27: *rspec-metadata-pg
-rspec-pg 26 27: *rspec-metadata-pg
-
-rspec-mysql 0 27: *rspec-metadata-mysql
-rspec-mysql 1 27: *rspec-metadata-mysql
-rspec-mysql 2 27: *rspec-metadata-mysql
-rspec-mysql 3 27: *rspec-metadata-mysql
-rspec-mysql 4 27: *rspec-metadata-mysql
-rspec-mysql 5 27: *rspec-metadata-mysql
-rspec-mysql 6 27: *rspec-metadata-mysql
-rspec-mysql 7 27: *rspec-metadata-mysql
-rspec-mysql 8 27: *rspec-metadata-mysql
-rspec-mysql 9 27: *rspec-metadata-mysql
-rspec-mysql 10 27: *rspec-metadata-mysql
-rspec-mysql 11 27: *rspec-metadata-mysql
-rspec-mysql 12 27: *rspec-metadata-mysql
-rspec-mysql 13 27: *rspec-metadata-mysql
-rspec-mysql 14 27: *rspec-metadata-mysql
-rspec-mysql 15 27: *rspec-metadata-mysql
-rspec-mysql 16 27: *rspec-metadata-mysql
-rspec-mysql 17 27: *rspec-metadata-mysql
-rspec-mysql 18 27: *rspec-metadata-mysql
-rspec-mysql 19 27: *rspec-metadata-mysql
-rspec-mysql 20 27: *rspec-metadata-mysql
-rspec-mysql 21 27: *rspec-metadata-mysql
-rspec-mysql 22 27: *rspec-metadata-mysql
-rspec-mysql 23 27: *rspec-metadata-mysql
-rspec-mysql 24 27: *rspec-metadata-mysql
-rspec-mysql 25 27: *rspec-metadata-mysql
-rspec-mysql 26 27: *rspec-metadata-mysql
-
-spinach-pg 0 3: *spinach-metadata-pg
-spinach-pg 1 3: *spinach-metadata-pg
-spinach-pg 2 3: *spinach-metadata-pg
-
-spinach-mysql 0 3: *spinach-metadata-mysql
-spinach-mysql 1 3: *spinach-metadata-mysql
-spinach-mysql 2 3: *spinach-metadata-mysql
+rspec-pg 0 28: *rspec-metadata-pg
+rspec-pg 1 28: *rspec-metadata-pg
+rspec-pg 2 28: *rspec-metadata-pg
+rspec-pg 3 28: *rspec-metadata-pg
+rspec-pg 4 28: *rspec-metadata-pg
+rspec-pg 5 28: *rspec-metadata-pg
+rspec-pg 6 28: *rspec-metadata-pg
+rspec-pg 7 28: *rspec-metadata-pg
+rspec-pg 8 28: *rspec-metadata-pg
+rspec-pg 9 28: *rspec-metadata-pg
+rspec-pg 10 28: *rspec-metadata-pg
+rspec-pg 11 28: *rspec-metadata-pg
+rspec-pg 12 28: *rspec-metadata-pg
+rspec-pg 13 28: *rspec-metadata-pg
+rspec-pg 14 28: *rspec-metadata-pg
+rspec-pg 15 28: *rspec-metadata-pg
+rspec-pg 16 28: *rspec-metadata-pg
+rspec-pg 17 28: *rspec-metadata-pg
+rspec-pg 18 28: *rspec-metadata-pg
+rspec-pg 19 28: *rspec-metadata-pg
+rspec-pg 20 28: *rspec-metadata-pg
+rspec-pg 21 28: *rspec-metadata-pg
+rspec-pg 22 28: *rspec-metadata-pg
+rspec-pg 23 28: *rspec-metadata-pg
+rspec-pg 24 28: *rspec-metadata-pg
+rspec-pg 25 28: *rspec-metadata-pg
+rspec-pg 26 28: *rspec-metadata-pg
+rspec-pg 27 28: *rspec-metadata-pg
+
+rspec-mysql 0 28: *rspec-metadata-mysql
+rspec-mysql 1 28: *rspec-metadata-mysql
+rspec-mysql 2 28: *rspec-metadata-mysql
+rspec-mysql 3 28: *rspec-metadata-mysql
+rspec-mysql 4 28: *rspec-metadata-mysql
+rspec-mysql 5 28: *rspec-metadata-mysql
+rspec-mysql 6 28: *rspec-metadata-mysql
+rspec-mysql 7 28: *rspec-metadata-mysql
+rspec-mysql 8 28: *rspec-metadata-mysql
+rspec-mysql 9 28: *rspec-metadata-mysql
+rspec-mysql 10 28: *rspec-metadata-mysql
+rspec-mysql 11 28: *rspec-metadata-mysql
+rspec-mysql 12 28: *rspec-metadata-mysql
+rspec-mysql 13 28: *rspec-metadata-mysql
+rspec-mysql 14 28: *rspec-metadata-mysql
+rspec-mysql 15 28: *rspec-metadata-mysql
+rspec-mysql 16 28: *rspec-metadata-mysql
+rspec-mysql 17 28: *rspec-metadata-mysql
+rspec-mysql 18 28: *rspec-metadata-mysql
+rspec-mysql 19 28: *rspec-metadata-mysql
+rspec-mysql 20 28: *rspec-metadata-mysql
+rspec-mysql 21 28: *rspec-metadata-mysql
+rspec-mysql 22 28: *rspec-metadata-mysql
+rspec-mysql 23 28: *rspec-metadata-mysql
+rspec-mysql 24 28: *rspec-metadata-mysql
+rspec-mysql 25 28: *rspec-metadata-mysql
+rspec-mysql 26 28: *rspec-metadata-mysql
+rspec-mysql 27 28: *rspec-metadata-mysql
+
+spinach-pg 0 2: *spinach-metadata-pg
+spinach-pg 1 2: *spinach-metadata-pg
+
+spinach-mysql 0 2: *spinach-metadata-mysql
+spinach-mysql 1 2: *spinach-metadata-mysql
# Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis
@@ -607,22 +607,24 @@ karma:
codequality:
<<: *except-docs
<<: *pull-cache
- before_script: []
- image: docker:latest
stage: test
- variables:
- SETUP_DB: "false"
- DOCKER_DRIVER: overlay
+ image: docker:latest
+ before_script: []
services:
- docker:dind
+ variables:
+ SETUP_DB: "false"
+ DOCKER_DRIVER: overlay2
+ CODECLIMATE_FORMAT: json
+ cache: {}
+ dependencies: []
script:
- - cp .rubocop.yml .rubocop.yml.bak
- - grep -v "rubocop-gitlab-security" .rubocop.yml.bak > .rubocop.yml
- - docker run --env CODECLIMATE_CODE="$PWD" --volume "$PWD":/code --volume /var/run/docker.sock:/var/run/docker.sock --volume /tmp/cc:/tmp/cc dev.gitlab.org:5005/gitlab/gitlab-build-images:gitlab-codeclimate-v2 analyze -f json > raw_codeclimate.json
- - cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,fingerprint,location})' > codeclimate.json
- - mv .rubocop.yml.bak .rubocop.yml
+ - ./scripts/codequality analyze -f json > raw_codeclimate.json || true
+ # The following line keeps only the fields used in the MR widget, reducing the JSON artifact size
+ - cat raw_codeclimate.json | docker run -i stedolan/jq -c 'map({check_name,description,fingerprint,location})' > codeclimate.json
artifacts:
paths: [codeclimate.json]
+ expire_in: 1 week
sast:
<<: *except-docs
diff --git a/.rubocop.yml b/.rubocop.yml
index 563a00db6c0..24edb641657 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -10,15 +10,26 @@ AllCops:
Exclude:
- 'vendor/**/*'
- 'node_modules/**/*'
- - 'db/*'
+ - 'db/**/*'
- 'db/fixtures/**/*'
- - 'db/geo/*'
+ - 'ee/db/**/*'
- 'tmp/**/*'
- 'bin/**/*'
- 'generator_templates/**/*'
- 'builds/**/*'
CacheRootDirectory: tmp
+# This cop checks whether some constant value isn't a
+# mutable literal (e.g. array or hash).
+Style/MutableConstant:
+ Enabled: true
+ Exclude:
+ - 'db/migrate/**/*'
+ - 'db/post_migrate/**/*'
+ - 'ee/db/migrate/**/*'
+ - 'ee/db/post_migrate/**/*'
+ - 'ee/db/geo/migrate/**/*'
+
# Gitlab ###################################################################
Gitlab/ModuleWithInstanceVariables:
@@ -33,3 +44,16 @@ Gitlab/ModuleWithInstanceVariables:
# We ignore spec helpers because it usually doesn't matter
- spec/support/**/*.rb
- features/steps/**/*.rb
+
+GitlabSecurity/PublicSend:
+ Enabled: true
+ Exclude:
+ - 'config/**/*'
+ - 'db/**/*'
+ - 'features/**/*'
+ - 'lib/**/*.rake'
+ - 'qa/**/*'
+ - 'spec/**/*'
+ - 'ee/db/**/*'
+ - 'ee/lib/**/*.rake'
+ - 'ee/spec/**/*'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9ad603fdc75..c8d399b2b98 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,242 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.5.2 (2018-02-25)
+
+### Fixed (7 changes)
+
+- Fix single digit value clipping for stacked progress bar. !17217
+- Fix issue with cache key being empty when variable used as the key. !17260
+- Enable Legacy Authorization by default on Cluster creations. !17302
+- Allow branch names to be named the same as the sha it points to.
+- Fix 500 error when loading an invalid upload URL.
+- Don't attempt to update user tracked fields if database is in read-only.
+- Prevent MR Widget error when no CI configured.
+
+### Performance (5 changes)
+
+- Improve query performance for snippets dashboard. !17088
+- Only check LFS integrity for first ref in a push to avoid timeout. !17098
+- Improve query performance of MembersFinder. !17190
+- Increase feature flag cache TTL to one hour.
+- Improve performance of searching for and autocompleting of users.
+
+
+## 10.5.1 (2018-02-22)
+
+- No changes.
+
+## 10.5.0 (2018-02-22)
+
+### Security (3 changes, 1 of them is from the community)
+
+- Update marked from 0.3.6 to 0.3.12. !16480 (Takuya Noguchi)
+- Update nokogiri to 1.8.2. !16807
+- Add verification for GitLab Pages custom domains.
+
+### Fixed (77 changes, 25 of them are from the community)
+
+- Fix the Projects API with_issues_enabled filter behaving incorrectly any user. !12724 (Jan Christophersen)
+- Hide pipeline schedule take ownership for current owner. !12986
+- Handle special characters on API request of issuable templates. !15323 (Takuya Noguchi)
+- Shows signin tab after new user email confirmation. !16174 (Jacopo Beschi @jacopo-beschi)
+- Make project README containers wider on fixed layout. !16181 (Takuya Noguchi)
+- Fix dashboard projects nav links height. !16204 (George Tsiolis)
+- Fix error on empty query for Members API. !16235
+- Issue board: fix for dragging an issue to the very bottom in long lists. !16250 (David Kuri)
+- Make rich blob viewer wider for PC. !16262 (Takuya Noguchi)
+- Substitute deprecated ui_charcoal with new default ui_indigo. !16271 (Takuya Noguchi)
+- Generate HTTP URLs for custom Pages domains when appropriate. !16279
+- Make modal dialog common for Groups tree app. !16311
+- Allow moving wiki pages from the UI. !16313
+- Filter groups and projects dropdowns of search page on backend. !16336
+- Adjust layout width for fixed layout. !16337 (George Tsiolis)
+- Fix custom header logo design nitpick: Remove unneeded margin on empty logo text. !16383 (Markus Doits)
+- File Upload UI can create LFS pointers based on .gitattributes. !16412
+- Fix Ctrl+Enter keyboard shortcut saving comment/note edit. !16415
+- Fix file search results when they match file contents with a number between two colons. !16462
+- Fix tooltip displayed for running manual actions. !16489
+- Allow trailing + on labels in board filters. !16490
+- Prevent JIRA issue identifier from being humanized. !16491 (Andrew McCallum)
+- Add horizontal scroll to wiki tables. !16527 (George Tsiolis)
+- Fix a bug calculating artifact size for project statistics. !16539
+- Stop loading spinner on error of issuable templates. !16600 (Takuya Noguchi)
+- Allows html text in commits atom feed. !16603 (Jacopo Beschi @jacopo-beschi)
+- Disable MR check out button when source branch is deleted. !16631 (Jacopo Beschi @jacopo-beschi)
+- Fix export removal for hashed-storage projects within a renamed or deleted namespace. !16658
+- Default to HTTPS for all Gravatar URLs. !16666
+- Login via OAuth now only marks new users as external. !16672
+- Fix default avatar icon missing when Gravatar is disabled. !16681 (Felix Geyer)
+- Change button group width on mobile. !16726 (George Tsiolis)
+- Fix version information not showing on help page if commercial content display was disabled. !16743
+- Adds spacing between edit and delete tag btn in tag list. !16757 (Jacopo Beschi @jacopo-beschi)
+- Fix 500 error when loading a merge request with an invalid comment. !16795
+- Deleting an upload will correctly clean up the filesystem. !16799
+- Cleanup new branch/merge request form in issues. !16854
+- Fix GitLab import leaving group_id on ProjectLabel. !16877
+- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881
+- Trigger change event on filename input when file template is applied. !16911 (Sebastian Klingler)
+- Fixes different margins between buttons in tag list. !16927 (Jacopo Beschi @jacopo-beschi)
+- Close low level rugged repository in project cache worker. !16930 (Bastian Blank)
+- Override group sidebar links. !16942 (George Tsiolis)
+- Avoid running `PopulateForkNetworksRange`-migration multiple times. !16988
+- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019
+- Fix monaco editor features which were incompatable with GitLab CDN settings. !17021
+- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054
+- Fix cnacel edit note button reverting changes. !42462
+- For issues display time of last edit of title or description instead of time of any attribute change.
+- Handle all Psych YAML parser exceptions (fixes #41209).
+- Fix validation of environment scope of variables.
+- Display user friendly error message if rebase fails.
+- Hide new branch and tag links for projects with an empty repo.
+- Fix protected branches API to accept name parameter with dot.
+- Closes #38540 - Remove .ssh/environment file that now breaks the gitlab:check rake task.
+- Keep subscribers when promoting labels to group labels.
+- Replace verified badge icons and uniform colors.
+- Fix error on changes tab when merge request cannot be created.
+- Ignore leading slashes when searching for files within context of repository. (Andrew McCallum)
+- Close and do not reload MR diffs when source branch is deleted.
+- Bypass commits title markdown on notes.
+- Reload MRs memoization after diffs creation.
+- Return more consistent values for merge_status on MR APIs.
+- Contribution calendar label was cut off. (Branka Martinovic)
+- LDAP Person no longer throws exception on invalid entry.
+- Fix bug where award emojis would be lost when moving issues between projects.
+- Fix not all events being shown in group dashboard.
+- Fix JIRA not working when a trailing slash is included.
+- Fix squash not working when diff contained non-ASCII data.
+- Remove erroneous text in shared runners page that suggested more runners available.
+- Execute system hooks after-commit when executing project hooks.
+- Makes forking protect default branch on completion.
+- Validate user, group and project paths consistently, and only once.
+- Validate user namespace before saving so that errors persist on model.
+- Permits 'password_authentication_enabled_for_git' parameter for ApplicationSettingsController.
+- Fix duplicate item in protected branch/tag dropdown.
+- Open visibility level help in a new tab. (Jussi Räsänen)
+
+### Deprecated (1 change)
+
+- Add note within ux documentation that further changes should be made within the design.gitlab project.
+
+### Changed (20 changes, 7 of them are from the community)
+
+- Show coverage to two decimal points in coverage badge. !10083 (Jeff Stubler)
+- Update 'removed assignee' note to include old assignee reference. !16301 (Maurizio De Santis)
+- Move row containing Projects, Users and Groups count to the top in admin dashboard. !16421
+- Add Auto DevOps Domain application setting. !16604
+- Changes Revert this merge request text. !16611 (Jacopo Beschi @jacopo-beschi)
+- Link Auto DevOps settings to Clusters page. !16641
+- Internationalize charts page. !16687 (selrahman)
+- Internationalize graph page selrahman. !16688 (Shah El-Rahman)
+- Save traces as artifacts. !16702
+- Hide variable values on pipeline schedule edit page. !16729
+- Update runner info on all authenticated requests. !16756
+- Improve issue note dropdown and mr button. !16758 (George Tsiolis)
+- Replace "cluster" with "Kubernetes cluster". !16778
+- Enable Prometheus metrics for deployed Ingresses. !16866 (joshlambert)
+- Rename button to enable CI/CD configuration to "Set up CI/CD". !16870
+- Double padding for file-content wiki class on larger screens.
+- Improve wording about additional costs for Ingress on custom clusters.
+- Last push widget will show banner for new pushes to previously merged branch.
+- Save user ID and username in Grape API log (api_json.log).
+- Include subgroup issues and merge requests on the group page.
+
+### Performance (14 changes, 1 of them is from the community)
+
+- Fix double query execution on groups page. !16314
+- Speed up loading merged merge requests when they contained a lot of commits before merging. !16320
+- Properly memoize some predicate methods. !16329
+- Reduce the number of Prometheus metrics. !16443
+- Only highlight search results under the highlighting size limit. !16462
+- Add fast-blank. !16468
+- Move BoardList vue component to vue file. !16888 (George Tsiolis)
+- Fix N+1 query problem for snippets dashboard. !16944
+- Optimize search queries on the search page by setting a limit for matching records.
+- Store number of commits in merge_request_diffs table.
+- Improve performance of target branch dropdown.
+- Remove duplicate calls of MergeRequest#can_be_reverted?.
+- Stop checking if discussions are in a mergeable state if the MR isn't.
+- Remove N+1 queries with /projects/:project_id/{access_requests,members} API endpoints.
+
+### Added (28 changes, 10 of them are from the community)
+
+- Add link on commit page to merge request that introduced that commit. !13713 (Hiroyuki Sato)
+- System hooks for Merge Requests. !14387 (Alexis Reigel)
+- Add `pipelines` endpoint to merge requests API. !15454 (Tony Rom <thetonyrom@gmail.com>)
+- Adds Rubocop rule for line break around conditionals. !15739 (Jacopo Beschi @jacopo-beschi)
+- Add Colors to GitLab Flavored Markdown. !16095 (Tony Rom <thetonyrom@gmail.com>)
+- Initial work to add notification reason to emails. !16160 (Mario de la Ossa)
+- Implement multi server support and use kube proxy to connect to Prometheus servers inside K8S cluster. !16182
+- Add ability to transfer a group into another group. !16302
+- Add blue dot feature highlight to make GKE Clusters more visible to users. !16379
+- Add section headers to plus button dropdown. !16394 (George Tsiolis)
+- Support PostgreSQL 10. !16471
+- Enables Project Milestone Deletion via the API. !16478 (Jacopo Beschi @jacopo-beschi)
+- Add realtime ci status for the repository -> files view. !16523
+- User can now git push to create a new project. !16547
+- Improve empty project overview. !16617 (George Tsiolis)
+- Added uploader metadata to the uploads. !16779
+- Added ldap config setting to lower case the username. !16791
+- Add search support into the API. !16878
+- Backport of LFS File Locking API. !16935
+- Add a link to documentation on how to get external ip in the Kubernetes cluster details page. !16937
+- Add sorting options for /users API (admin only). !16945
+- Adds sorting to deployments API. (Jacopo Beschi @jacopo-beschi)
+- Add rake task to check integrity of uploaded files.
+- Add backend for persistently dismissably callouts.
+- Track and act upon the number of executed queries.
+- Add a gRPC health check to ensure Gitaly is up.
+- Log and send a system hook if a blocked user attempts to login.
+- Add Gitaly Servers admin dashboard.
+
+### Other (25 changes, 7 of them are from the community)
+
+- Updated the katex library. !15864
+- Add modal for deleting a milestone. !16229
+- Remove unused CSS selectors for Cycle Analytics. !16270 (Takuya Noguchi)
+- Add reason to keep postgresql 9.2 for CI. !16277 (Takuya Noguchi)
+- Adjust modal style to new design. !16310
+- Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH pull. !16586
+- Set timezone for karma to UTC. !16602 (Takuya Noguchi)
+- Make Gitaly RepositoryExists opt-out. !16680
+- Update minimum git version to 2.9.5. !16683
+- Disable throwOnError in KaTeX to reveal user where is the problem. !16684 (Jakub Jirutka)
+- fix documentation about node version. !16720 (Tobias Gurtzick)
+- Enable RuboCop Style/RegexpLiteral. !16752 (Takuya Noguchi)
+- Add confirmation-input component. !16816
+- Add unique constraint to trending_projects#project_id. !16846
+- Add foreign key and NOT NULL constraints to todos table. !16849
+- Include branch in mobile view for pipelines. !16910 (George Tsiolis)
+- Downgrade google-protobuf gem. !16941
+- Refactors mr widget components into vue files and adds i18n.
+- increase-readability-of-colored-text-in-job-output-log.
+- Finish any remaining jobs for issues.closed_at.
+- Translate issuable sidebar.
+- Set standard disabled state for all buttons.
+- Upgrade GitLab Workhorse to v3.6.0.
+- Improve readability of underlined links for dyslexic users.
+- Adds empty state illustration for pending job.
+
+
+## 10.4.4 (2018-02-16)
+
+### Security (1 change)
+
+- Update nokogiri to 1.8.2. !16807
+
+### Fixed (9 changes)
+
+- Fix 500 error when loading a merge request with an invalid comment. !16795
+- Cleanup new branch/merge request form in issues. !16854
+- Fix GitLab import leaving group_id on ProjectLabel. !16877
+- Fix forking projects when no restricted visibility levels are defined applicationwide. !16881
+- Resolve PrepareUntrackedUploads PostgreSQL syntax error. !17019
+- Fixed error 500 when removing an identity with synced attributes and visiting the profile page. !17054
+- Validate user namespace before saving so that errors persist on model.
+- LDAP Person no longer throws exception on invalid entry.
+- Fix JIRA not working when a trailing slash is included.
+
+
## 10.4.3 (2018-02-05)
### Security (4 changes)
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ed56da0353d..b70d2da5bea 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -174,7 +174,7 @@ Assigning a team label makes sure issues get the attention of the appropriate
people.
The current team labels are ~Build, ~"CI/CD", ~Discussion, ~Documentation, ~Edge,
-~Geo, ~Gitaly, ~Platform, ~Monitoring, ~Release, and ~"UX".
+~Geo, ~Gitaly, ~Monitoring, ~Platform, ~Release, ~"Security Products" and ~"UX".
The descriptions on the [labels page][labels-page] explain what falls under the
responsibility of each team.
@@ -397,9 +397,9 @@ For issues related to the open source stewardship of GitLab,
there is the ~"stewardship" label.
This label is to be used for issues in which the stewardship of GitLab
-is a topic of discussion. For instance if GitLab Inc. is planning to remove
-features from GitLab CE to make exclusive in GitLab EE, related issues
-would be labelled with ~"stewardship".
+is a topic of discussion. For instance if GitLab Inc. is planning to add
+features from GitLab EE to GitLab CE, related issues would be labelled with
+~"stewardship".
A recent example of this was the issue for
[bringing the time tracking API to GitLab CE][time-tracking-issue].
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 9a55e28031d..137c1281121 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.81.0
+0.85.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 9b9a244206f..090ea9dad19 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-6.0.2
+6.0.3
diff --git a/Gemfile b/Gemfile
index 880ed483c34..6838ddbf01a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -81,7 +81,7 @@ gem 'gollum-lib', '~> 4.2', require: false
gem 'gollum-rugged_adapter', '~> 0.4.4', require: false
# Language detection
-gem 'github-linguist', '~> 4.7.0', require: 'linguist'
+gem 'github-linguist', '~> 5.3.3', require: 'linguist'
# API
gem 'grape', '~> 1.0'
@@ -401,6 +401,7 @@ gem 'sys-filesystem', '~> 1.1.6'
# SSH host key support
gem 'net-ssh', '~> 4.1.0'
+gem 'sshkey', '~> 1.9.0'
# Required for ED25519 SSH host key support
group :ed25519 do
@@ -410,11 +411,11 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.84.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.85.0', require: 'gitaly'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
gem 'google-protobuf', '= 3.5.1'
-gem 'toml-rb', '~> 0.3.15', require: false
+gem 'toml-rb', '~> 1.0.0', require: false
# Feature toggles
gem 'flipper', '~> 0.11.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 22c4fc0ef28..89b86ae0259 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -285,14 +285,14 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
- gitaly-proto (0.84.0)
+ gitaly-proto (0.85.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
- github-linguist (4.7.6)
- charlock_holmes (~> 0.7.3)
+ github-linguist (5.3.3)
+ charlock_holmes (~> 0.7.5)
escape_utils (~> 1.1.0)
mime-types (>= 1.19)
- rugged (>= 0.23.0b)
+ rugged (>= 0.25.1)
github-markup (1.6.1)
gitlab-flowdock-git-hook (1.0.1)
flowdock (~> 0.7)
@@ -895,6 +895,7 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
+ sshkey (1.9.0)
stackprof (0.2.10)
state_machines (0.4.0)
state_machines-activemodel (0.4.0)
@@ -923,7 +924,7 @@ GEM
timfel-krb5-auth (0.8.3)
toml (0.1.2)
parslet (~> 1.5.0)
- toml-rb (0.3.15)
+ toml-rb (1.0.0)
citrus (~> 3.0, > 3.0)
truncato (0.7.10)
htmlentities (~> 4.3.1)
@@ -1056,8 +1057,8 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
- gitaly-proto (~> 0.84.0)
- github-linguist (~> 4.7.0)
+ gitaly-proto (~> 0.85.0)
+ github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2)
gitlab-styles (~> 2.3)
@@ -1192,6 +1193,7 @@ DEPENDENCIES
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.1.0)
sprockets (~> 3.7.0)
+ sshkey (~> 1.9.0)
stackprof (~> 0.2.10)
state_machines-activerecord (~> 0.4.0)
sys-filesystem (~> 1.1.6)
@@ -1199,7 +1201,7 @@ DEPENDENCIES
test_after_commit (~> 1.1)
thin (~> 1.7.0)
timecop (~> 0.8.0)
- toml-rb (~> 0.3.15)
+ toml-rb (~> 1.0.0)
truncato (~> 0.7.9)
u2f (~> 0.2.1)
uglifier (~> 2.7.2)
diff --git a/PROCESS.md b/PROCESS.md
index 99af3be7f14..c24210341e0 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -71,11 +71,15 @@ star, smile, etc.). Some good tips about code reviews can be found in our
## Feature freeze on the 7th for the release on the 22nd
-After the 7th (Pacific Standard Time Zone) of each month, RC1 of the upcoming release (to be shipped on the 22nd) is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
+After 7th at 23:59 (Pacific Standard Time Zone) of each month, RC1 of the upcoming release (to be shipped on the 22nd) is created and deployed to GitLab.com and the stable branch for this release is frozen, which means master is no longer merged into it.
Merge requests may still be merged into master during this period,
but they will go into the _next_ release, unless they are manually cherry-picked into the stable branch.
+
By freezing the stable branches 2 weeks prior to a release, we reduce the risk of a last minute merge request potentially breaking things.
+Any release candidate that gets created after this date can become a final release,
+hence the name release candidate.
+
### Between the 1st and the 7th
These types of merge requests for the upcoming release need special consideration:
@@ -193,11 +197,10 @@ to be backported down to the `9.5` release, you will need to assign it the
### Asking for an exception
If you think a merge request should go into an RC or patch even though it does not meet these requirements,
-you can ask for an exception to be made. Exceptions require sign-off from 3 people besides the developer:
+you can ask for an exception to be made.
-1. a Release Manager
-2. an Engineering Lead
-3. an Engineering Director, the VP of Engineering, or the CTO
+Go to [Release tasks issue tracker](https://gitlab.com/gitlab-org/release/tasks/issues/new) and create an issue
+using the `Exception-request` issue template.
You can find who is who on the [team page](https://about.gitlab.com/team/).
diff --git a/VERSION b/VERSION
index 7f6774b38ba..f860e97e4cf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-10.5.0-pre
+10.6.0-pre
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 1f34c6b50c2..464611f66f0 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -9,7 +9,7 @@ const Api = {
projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels',
- groupLabelsPath: '/groups/:namespace_path/labels',
+ groupLabelsPath: '/groups/:namespace_path/-/labels',
licensePath: '/api/:version/templates/licenses/:key',
gitignorePath: '/api/:version/templates/gitignores/:key',
gitlabCiYmlPath: '/api/:version/templates/gitlab_ci_ymls/:key',
@@ -32,7 +32,7 @@ const Api = {
},
// Return groups list. Filtered by query
- groups(query, options, callback) {
+ groups(query, options, callback = $.noop) {
const url = Api.buildUrl(Api.groupsPath);
return axios.get(url, {
params: Object.assign({
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 87109a802e5..9456edebccb 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -50,10 +50,8 @@ class AwardsHandler {
this.registerEventListener('on', $('html'), 'click', (e) => {
const $target = $(e.target);
- if (!$target.closest('.emoji-menu-content').length) {
- $('.js-awards-block.current').removeClass('current');
- }
if (!$target.closest('.emoji-menu').length) {
+ $('.js-awards-block.current').removeClass('current');
if ($('.emoji-menu').is(':visible')) {
$('.js-add-award.is-active').removeClass('is-active');
this.hideMenuElement($('.emoji-menu'));
@@ -314,7 +312,7 @@ class AwardsHandler {
}
getAwardUrl() {
- return this.getVotesBlock().data('award-url');
+ return this.getVotesBlock().data('awardUrl');
}
checkMutuality(votesBlock, emoji) {
diff --git a/app/assets/javascripts/behaviors/copy_to_clipboard.js b/app/assets/javascripts/behaviors/copy_to_clipboard.js
index cdea625fc8c..b669b63d23c 100644
--- a/app/assets/javascripts/behaviors/copy_to_clipboard.js
+++ b/app/assets/javascripts/behaviors/copy_to_clipboard.js
@@ -2,7 +2,7 @@ import Clipboard from 'clipboard';
function showTooltip(target, title) {
const $target = $(target);
- const originalTitle = $target.data('original-title');
+ const originalTitle = $target.data('originalTitle');
if (!$target.data('hideTooltip')) {
$target
diff --git a/app/assets/javascripts/behaviors/quick_submit.js b/app/assets/javascripts/behaviors/quick_submit.js
index 2cf8f4fa935..312edc0cd69 100644
--- a/app/assets/javascripts/behaviors/quick_submit.js
+++ b/app/assets/javascripts/behaviors/quick_submit.js
@@ -43,7 +43,7 @@ $(document).on('keydown.quick_submit', '.js-quick-submit', (e) => {
const $form = $(e.target).closest('form');
const $submitButton = $form.find('input[type=submit], button[type=submit]').first();
- if (!$submitButton.attr('disabled')) {
+ if (!$submitButton.prop('disabled')) {
$submitButton.trigger('click', [e]);
if (!isInIssuePage()) {
diff --git a/app/assets/javascripts/behaviors/requires_input.js b/app/assets/javascripts/behaviors/requires_input.js
index 035a7e5c431..e10cb2e3dc4 100644
--- a/app/assets/javascripts/behaviors/requires_input.js
+++ b/app/assets/javascripts/behaviors/requires_input.js
@@ -40,7 +40,7 @@ $.fn.requiresInput = function requiresInput() {
// based on the option selected
function hideOrShowHelpBlock(form) {
const selected = $('.js-select-namespace option:selected');
- if (selected.length && selected.data('options-parent') === 'groups') {
+ if (selected.length && selected.data('optionsParent') === 'groups') {
form.find('.help-block').hide();
} else if (selected.length) {
form.find('.help-block').show();
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 6b06344f5ba..931ed042dfd 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -4,16 +4,16 @@ import NewCommitForm from '../new_commit_form';
import EditBlob from './edit_blob';
import BlobFileDropzone from '../blob/blob_file_dropzone';
-$(() => {
+export default () => {
const editBlobForm = $('.js-edit-blob-form');
const uploadBlobForm = $('.js-upload-blob-form');
const deleteBlobForm = $('.js-delete-blob-form');
if (editBlobForm.length) {
- const urlRoot = editBlobForm.data('relative-url-root');
- const assetsPath = editBlobForm.data('assets-prefix');
- const blobLanguage = editBlobForm.data('blob-language');
- const currentAction = $('.js-file-title').data('current-action');
+ const urlRoot = editBlobForm.data('relativeUrlRoot');
+ const assetsPath = editBlobForm.data('assetsPrefix');
+ const blobLanguage = editBlobForm.data('blobLanguage');
+ const currentAction = $('.js-file-title').data('currentAction');
new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage, currentAction);
new NewCommitForm(editBlobForm);
@@ -34,4 +34,4 @@ $(() => {
if (deleteBlobForm.length) {
new NewCommitForm(deleteBlobForm);
}
-});
+};
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index a25f7fb3dcd..d4f6adaccbc 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -59,7 +59,7 @@ export default class EditBlob {
if (paneId === '#preview') {
this.$toggleButton.hide();
- axios.post(currentLink.data('preview-url'), {
+ axios.post(currentLink.data('previewUrl'), {
content: this.editor.getValue(),
})
.then(({ data }) => {
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 9a0442e2afe..6637904d87d 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -1,6 +1,6 @@
<script>
import Sortable from 'vendor/Sortable';
-import boardNewIssue from './board_new_issue';
+import boardNewIssue from './board_new_issue.vue';
import boardCard from './board_card.vue';
import eventHub from '../eventhub';
import loadingIcon from '../../vue_shared/components/loading_icon.vue';
diff --git a/app/assets/javascripts/boards/components/board_new_issue.js b/app/assets/javascripts/boards/components/board_new_issue.vue
index bc28f7f45f4..efface7143d 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.js
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -1,5 +1,6 @@
-/* global ListIssue */
+<script>
import eventHub from '../eventhub';
+import ListIssue from '../models/issue';
const Store = gl.issueBoards.BoardsStore;
@@ -17,6 +18,9 @@ export default {
error: false,
};
},
+ mounted() {
+ this.$refs.input.focus();
+ },
methods: {
submit(e) {
e.preventDefault();
@@ -59,42 +63,51 @@ export default {
eventHub.$emit(`hide-issue-form-${this.list.id}`);
},
},
- mounted() {
- this.$refs.input.focus();
- },
- template: `
- <div class="card board-new-issue-form">
- <form @submit="submit($event)">
- <div class="flash-container"
- v-if="error">
- <div class="flash-alert">
- An error occurred. Please try again.
- </div>
- </div>
- <label class="label-light"
- :for="list.id + '-title'">
- Title
- </label>
- <input class="form-control"
- type="text"
- v-model="title"
- ref="input"
- autocomplete="off"
- :id="list.id + '-title'" />
- <div class="clearfix prepend-top-10">
- <button class="btn btn-success pull-left"
- type="submit"
- :disabled="title === ''"
- ref="submit-button">
- Submit issue
- </button>
- <button class="btn btn-default pull-right"
- type="button"
- @click="cancel">
- Cancel
- </button>
- </div>
- </form>
- </div>
- `,
};
+</script>
+
+<template>
+ <div class="card board-new-issue-form">
+ <form @submit="submit($event)">
+ <div
+ class="flash-container"
+ v-if="error"
+ >
+ <div class="flash-alert">
+ An error occurred. Please try again.
+ </div>
+ </div>
+ <label
+ class="label-light"
+ :for="list.id + '-title'"
+ >
+ Title
+ </label>
+ <input
+ class="form-control"
+ type="text"
+ v-model="title"
+ ref="input"
+ autocomplete="off"
+ :id="list.id + '-title'"
+ />
+ <div class="clearfix prepend-top-10">
+ <button
+ class="btn btn-success pull-left"
+ type="submit"
+ :disabled="title === ''"
+ ref="submit-button"
+ >
+ Submit issue
+ </button>
+ <button
+ class="btn btn-default pull-right"
+ type="button"
+ @click="cancel"
+ >
+ Cancel
+ </button>
+ </div>
+ </form>
+ </div>
+</template>
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 983429550f0..add24303e7b 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -2,6 +2,7 @@
import Vue from 'vue';
import Flash from '../../flash';
+import { __ } from '../../locale';
import Sidebar from '../../right_sidebar';
import eventHub from '../../sidebar/event_hub';
import assigneeTitle from '../../sidebar/components/assignees/assignee_title';
@@ -95,7 +96,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({
})
.catch(() => {
this.loadingAssignees = false;
- return new Flash('An error occurred while saving assignees');
+ Flash(__('An error occurred while saving assignees'));
});
},
},
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index 182957113a2..03cd7ef65cb 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -1,7 +1,6 @@
-/* eslint-disable no-new */
-
import Vue from 'vue';
import Flash from '../../../flash';
+import { __ } from '../../../locale';
import './lists_dropdown';
import { pluralize } from '../../../lib/utils/text_utility';
@@ -36,7 +35,7 @@ gl.issueBoards.ModalFooter = Vue.extend({
gl.boardService.bulkUpdate(issueIds, {
add_label_ids: [list.label.id],
}).catch(() => {
- new Flash('Failed to update issues, please try again.', 'alert');
+ Flash(__('Failed to update issues, please try again.'));
selectedIssues.forEach((issue) => {
list.removeIssue(issue);
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index c19c989680d..362ef43e6f7 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, no-new, space-before-function-paren, one-var,
promise/catch-or-return */
+import axios from '~/lib/utils/axios_utils';
import _ from 'underscore';
import CreateLabelDropdown from '../../create_label';
@@ -24,13 +25,13 @@ $(document).off('created.label').on('created.label', (e, label) => {
gl.issueBoards.newListDropdownInit = () => {
$('.js-new-board-list').each(function () {
const $this = $(this);
- new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespace-path'), $this.data('project-path'));
+ new CreateLabelDropdown($this.closest('.dropdown').find('.dropdown-new-label'), $this.data('namespacePath'), $this.data('projectPath'));
$this.glDropdown({
data(term, callback) {
- $.get($this.attr('data-list-labels-path'))
- .then((resp) => {
- callback(resp);
+ axios.get($this.attr('data-list-labels-path'))
+ .then(({ data }) => {
+ callback(data);
});
},
renderRow (label) {
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
index 1ad97211934..0ae32bb4d0a 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
@@ -1,7 +1,6 @@
-/* eslint-disable no-new */
-
import Vue from 'vue';
import Flash from '../../../flash';
+import { __ } from '../../../locale';
const Store = gl.issueBoards.BoardsStore;
@@ -45,7 +44,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
},
};
Vue.http.patch(this.updateUrl, data).catch(() => {
- new Flash('Failed to remove issue from board, please try again.', 'alert');
+ Flash(__('Failed to remove issue from board, please try again.'));
lists.forEach((list) => {
list.addIssue(issue);
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 184665f395c..0df1f7a6f82 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -1,7 +1,8 @@
/* eslint-disable class-methods-use-this */
import FilteredSearchContainer from '../filtered_search/container';
+import FilteredSearchManager from '../filtered_search/filtered_search_manager';
-export default class FilteredSearchBoards extends gl.FilteredSearchManager {
+export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
super('boards');
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/index.js
index 90166b3d3d1..8e31f1865f0 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/index.js
@@ -2,11 +2,13 @@
import _ from 'underscore';
import Vue from 'vue';
-import Flash from '../flash';
-import { __ } from '../locale';
+
+import Flash from '~/flash';
+import { __ } from '~/locale';
+
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
-import sidebarEventHub from '../sidebar/event_hub';
+import sidebarEventHub from '~/sidebar/event_hub'; // eslint-disable-line import/first
import './models/issue';
import './models/label';
import './models/list';
@@ -22,9 +24,9 @@ import './components/board';
import './components/board_sidebar';
import './components/new_list_dropdown';
import './components/modal/index';
-import '../vue_shared/vue_resource_interceptor';
+import '~/vue_shared/vue_resource_interceptor'; // eslint-disable-line import/first
-$(() => {
+export default () => {
const $boardApp = document.getElementById('board-app');
const Store = gl.issueBoards.BoardsStore;
const ModalStore = gl.issueBoards.ModalStore;
@@ -236,4 +238,4 @@ $(() => {
</div>
`,
});
-});
+};
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index 81edd95bf2b..3bfb6d39ad5 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -110,3 +110,5 @@ class ListIssue {
}
window.ListIssue = ListIssue;
+
+export default ListIssue;
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 798d7e0d147..348cdeec737 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -2,7 +2,7 @@
/* global List */
import _ from 'underscore';
import Cookies from 'js-cookie';
-import { getUrlParamsArray } from '../../lib/utils/common_utils';
+import { getUrlParamsArray } from '~/lib/utils/common_utils';
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
index 76f93e5c6bd..b33adff609f 100644
--- a/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ajax_variable_list.js
@@ -75,6 +75,7 @@ export default class AjaxVariableList {
if (res.status === statusCodes.OK && res.data) {
this.updateRowsWithPersistedVariables(res.data.variables);
+ this.variableList.hideValues();
} else if (res.status === statusCodes.BAD_REQUEST) {
// Validation failed
this.errorBox.innerHTML = generateErrorBoxContent(res.data);
diff --git a/app/assets/javascripts/ci_variable_list/ci_variable_list.js b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
index d91789c2192..745f3404295 100644
--- a/app/assets/javascripts/ci_variable_list/ci_variable_list.js
+++ b/app/assets/javascripts/ci_variable_list/ci_variable_list.js
@@ -39,7 +39,7 @@ export default class VariableList {
},
protected: {
selector: '.js-ci-variable-input-protected',
- default: 'true',
+ default: 'false',
},
environment_scope: {
// We can't use a `.js-` class here because
@@ -178,6 +178,10 @@ export default class VariableList {
this.$container.find('.js-row-remove-button').attr('disabled', !isEnabled);
}
+ hideValues() {
+ this.secretValues.updateDom(false);
+ }
+
getAllData() {
// Ignore the last empty row because we don't want to try persist
// a blank variable and run into validation problems.
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js
index 525fbf9dac9..6504a0bbbfc 100644
--- a/app/assets/javascripts/commit/image_file.js
+++ b/app/assets/javascripts/commit/image_file.js
@@ -1,5 +1,4 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
-import 'vendor/jquery.waitforimages';
// Width where images must fits in, for 2-up this gets divided by 2
const availWidth = 900;
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index 1f9153d95bd..3d89bf1316e 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -15,7 +15,7 @@ const CommitPipelinesTable = Vue.extend(commitPipelinesTable);
window.gl = window.gl || {};
window.gl.CommitPipelinesTable = CommitPipelinesTable;
-document.addEventListener('DOMContentLoaded', () => {
+export default () => {
const pipelineTableViewEl = document.querySelector('#commit-pipeline-table-view');
if (pipelineTableViewEl) {
@@ -43,4 +43,4 @@ document.addEventListener('DOMContentLoaded', () => {
pipelineTableViewEl.appendChild(table.$el);
}
}
-});
+};
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index da0e8063ccb..ce19069f103 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -7,7 +7,6 @@
mixins: [
pipelinesMixin,
],
-
props: {
endpoint: {
type: String,
diff --git a/app/assets/javascripts/commits.js b/app/assets/javascripts/commits.js
index 4b2f75fffde..2be63bd8c76 100644
--- a/app/assets/javascripts/commits.js
+++ b/app/assets/javascripts/commits.js
@@ -1,52 +1,36 @@
-/* eslint-disable func-names, wrap-iife, consistent-return,
- no-return-assign, no-param-reassign, one-var-declaration-per-line, no-unused-vars,
- prefer-template, object-shorthand, prefer-arrow-callback */
-
import { pluralize } from './lib/utils/text_utility';
import { localTimeAgo } from './lib/utils/datetime_utility';
import Pager from './pager';
import axios from './lib/utils/axios_utils';
-export default (function () {
- const CommitsList = {};
-
- CommitsList.timer = null;
+export default class CommitsList {
+ constructor(limit = 0) {
+ this.timer = null;
- CommitsList.init = function (limit) {
this.$contentList = $('.content_list');
- $('body').on('click', '.day-commits-table li.commit', function (e) {
- if (e.target.nodeName !== 'A') {
- location.href = $(this).attr('url');
- e.stopPropagation();
- return false;
- }
- });
-
- Pager.init(parseInt(limit, 10), false, false, this.processCommits);
+ Pager.init(parseInt(limit, 10), false, false, this.processCommits.bind(this));
this.content = $('#commits-list');
this.searchField = $('#commits-search');
this.lastSearch = this.searchField.val();
- return this.initSearch();
- };
+ this.initSearch();
+ }
- CommitsList.initSearch = function () {
+ initSearch() {
this.timer = null;
- return this.searchField.keyup((function (_this) {
- return function () {
- clearTimeout(_this.timer);
- return _this.timer = setTimeout(_this.filterResults, 500);
- };
- })(this));
- };
+ this.searchField.on('keyup', () => {
+ clearTimeout(this.timer);
+ this.timer = setTimeout(this.filterResults.bind(this), 500);
+ });
+ }
- CommitsList.filterResults = function () {
+ filterResults() {
const form = $('.commits-search-form');
- const search = CommitsList.searchField.val();
- if (search === CommitsList.lastSearch) return Promise.resolve();
- const commitsUrl = form.attr('action') + '?' + form.serialize();
- CommitsList.content.fadeTo('fast', 0.5);
+ const search = this.searchField.val();
+ if (search === this.lastSearch) return Promise.resolve();
+ const commitsUrl = `${form.attr('action')}?${form.serialize()}`;
+ this.content.fadeTo('fast', 0.5);
const params = form.serializeArray().reduce((acc, obj) => Object.assign(acc, {
[obj.name]: obj.value,
}), {});
@@ -55,9 +39,9 @@ export default (function () {
params,
})
.then(({ data }) => {
- CommitsList.lastSearch = search;
- CommitsList.content.html(data.html);
- CommitsList.content.fadeTo('fast', 1.0);
+ this.lastSearch = search;
+ this.content.html(data.html);
+ this.content.fadeTo('fast', 1.0);
// Change url so if user reload a page - search results are saved
history.replaceState({
@@ -65,16 +49,16 @@ export default (function () {
}, document.title, commitsUrl);
})
.catch(() => {
- CommitsList.content.fadeTo('fast', 1.0);
- CommitsList.lastSearch = null;
+ this.content.fadeTo('fast', 1.0);
+ this.lastSearch = null;
});
- };
+ }
// Prepare loaded data.
- CommitsList.processCommits = (data) => {
+ processCommits(data) {
let processedData = data;
const $processedData = $(processedData);
- const $commitsHeadersLast = CommitsList.$contentList.find('li.js-commit-header').last();
+ const $commitsHeadersLast = this.$contentList.find('li.js-commit-header').last();
const lastShownDay = $commitsHeadersLast.data('day');
const $loadedCommitsHeadersFirst = $processedData.filter('li.js-commit-header').first();
const loadedShownDayFirst = $loadedCommitsHeadersFirst.data('day');
@@ -97,7 +81,5 @@ export default (function () {
localTimeAgo($processedData.find('.js-timeago'));
return processedData;
- };
-
- return CommitsList;
-})();
+ }
+}
diff --git a/app/assets/javascripts/commons/bootstrap.js b/app/assets/javascripts/commons/bootstrap.js
index c11b7d5f340..db96da4ccba 100644
--- a/app/assets/javascripts/commons/bootstrap.js
+++ b/app/assets/javascripts/commons/bootstrap.js
@@ -13,6 +13,6 @@ import 'bootstrap-sass/assets/javascripts/bootstrap/popover';
// custom jQuery functions
$.fn.extend({
- disable() { return $(this).attr('disabled', 'disabled').addClass('disabled'); },
- enable() { return $(this).removeAttr('disabled').removeClass('disabled'); },
+ disable() { return $(this).prop('disabled', true).addClass('disabled'); },
+ enable() { return $(this).prop('disabled', false).removeClass('disabled'); },
});
diff --git a/app/assets/javascripts/commons/jquery.js b/app/assets/javascripts/commons/jquery.js
index b93e94a3c97..a7ed175f7a4 100644
--- a/app/assets/javascripts/commons/jquery.js
+++ b/app/assets/javascripts/commons/jquery.js
@@ -6,5 +6,5 @@ import 'vendor/jquery.endless-scroll';
import 'vendor/jquery.caret';
import 'vendor/jquery.atwho';
import 'vendor/jquery.scrollTo';
-import 'vendor/jquery.waitforimages';
+import 'jquery.waitforimages';
import 'select2/select2';
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index e2a008e8904..d5a35ed81a6 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -13,7 +13,7 @@ export default class Compare {
$dropdown = $(dropdown);
return $dropdown.glDropdown({
selectable: true,
- fieldName: $dropdown.data('field-name'),
+ fieldName: $dropdown.data('fieldName'),
filterable: true,
id: function(obj, $el) {
return $el.data('id');
diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js
index 59899e97be1..fa341918fc1 100644
--- a/app/assets/javascripts/compare_autocomplete.js
+++ b/app/assets/javascripts/compare_autocomplete.js
@@ -9,7 +9,7 @@ export default function initCompareAutocomplete() {
$dropdown = $(this);
selected = $dropdown.data('selected');
const $dropdownContainer = $dropdown.closest('.dropdown');
- const $fieldInput = $(`input[name="${$dropdown.data('field-name')}"]`, $dropdownContainer);
+ const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({
data: function(term, callback) {
@@ -25,7 +25,7 @@ export default function initCompareAutocomplete() {
selectable: true,
filterable: true,
filterRemote: true,
- fieldName: $dropdown.data('field-name'),
+ fieldName: $dropdown.data('fieldName'),
filterInput: 'input[type="search"]',
renderRow: function(ref) {
var link;
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index 482d83621e2..fb1fc9cd32e 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -180,6 +180,7 @@ export default class CreateMergeRequestDropdown {
valueAttribute: 'data-text',
},
],
+ hideOnClick: false,
};
}
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
index 39b699a6395..34aa04083e6 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
@@ -37,7 +37,7 @@
>
<div class="item-details">
<!-- FIXME: Pass an alt attribute here for accessibility -->
- <user-avatar-image :img-src="mergeRequest.author.avatarUrl"/>
+ <user-avatar-image :img-src="mergeRequest.author.avatarUrl" />
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
diff --git a/app/assets/javascripts/deploy_keys/index.js b/app/assets/javascripts/deploy_keys/index.js
index ca8798facc9..b727261648c 100644
--- a/app/assets/javascripts/deploy_keys/index.js
+++ b/app/assets/javascripts/deploy_keys/index.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import deployKeysApp from './components/app.vue';
-document.addEventListener('DOMContentLoaded', () => new Vue({
+export default () => new Vue({
el: document.getElementById('js-deploy-keys'),
components: {
deployKeysApp,
@@ -18,4 +18,4 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
},
});
},
-}));
+});
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index a162424b3cf..3df082e8c0c 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -1,3 +1,6 @@
+import axios from '~/lib/utils/axios_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
import { getLocationHash } from './lib/utils/url_utility';
import FilesCommentButton from './files_comment_button';
import SingleFileDiff from './single_file_diff';
@@ -65,11 +68,13 @@ export default class Diff {
}
const file = $target.parents('.diff-file');
- const link = file.data('blob-diff-path');
+ const link = file.data('blobDiffPath');
const view = file.data('view');
const params = { since, to, bottom, offset, unfold, view };
- $.get(link, params, response => $target.parent().replaceWith(response));
+ axios.get(link, { params })
+ .then(({ data }) => $target.parent().replaceWith(data))
+ .catch(() => flash(__('An error occurred while loading diff')));
}
openAnchoredDiff(cb) {
@@ -116,7 +121,7 @@ export default class Diff {
}
// eslint-disable-next-line class-methods-use-this
diffViewType() {
- return $('.inline-parallel-buttons a.active').data('view-type');
+ return $('.inline-parallel-buttons a.active').data('viewType');
}
// eslint-disable-next-line class-methods-use-this
lineNumbers(line) {
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
index e0422057090..679057e787c 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
@@ -15,7 +15,7 @@ import './components/resolve_discussion_btn';
import './components/diff_note_avatars';
import './components/new_issue_for_discussion';
-$(() => {
+export default () => {
const projectPathHolder = document.querySelector('.merge-request') || document.querySelector('.commit-box');
const projectPath = projectPathHolder.dataset.projectPath;
const COMPONENT_SELECTOR = 'resolve-btn, resolve-discussion-btn, jump-to-discussion, comment-and-resolve-btn, new-issue-for-discussion-btn';
@@ -71,8 +71,8 @@ $(() => {
el: '#resolve-count-app',
components: {
'resolve-count': ResolveCount
- }
+ },
});
$(window).trigger('resize.nav');
-});
+};
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index aceaffdfcb9..1ccf96a75dc 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -1,648 +1,85 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
-import MergeRequest from './merge_request';
import Flash from './flash';
import GfmAutoComplete from './gfm_auto_complete';
-import ZenMode from './zen_mode';
-import initNotes from './init_notes';
-import initIssuableSidebar from './init_issuable_sidebar';
import { convertPermissionToBoolean } from './lib/utils/common_utils';
import GlFieldErrors from './gl_field_errors';
import Shortcuts from './shortcuts';
-import ShortcutsIssuable from './shortcuts_issuable';
-import Diff from './diff';
import SearchAutocomplete from './search_autocomplete';
-var Dispatcher;
-
-(function() {
- Dispatcher = (function() {
- function Dispatcher() {
- this.initSearch();
- this.initFieldErrors();
- this.initPageScripts();
- }
-
- Dispatcher.prototype.initPageScripts = function() {
- var path, shortcut_handler;
- const page = $('body').attr('data-page');
- if (!page) {
- return false;
- }
-
- const fail = () => Flash('Error loading dynamic module');
- const callDefault = m => m.default();
-
- path = page.split(':');
- shortcut_handler = null;
-
- $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
- const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
- const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete);
- gfm.setup($(el), {
- emojis: true,
- members: enableGFM,
- issues: enableGFM,
- milestones: enableGFM,
- mergeRequests: enableGFM,
- labels: enableGFM,
- });
- });
-
- switch (page) {
- case 'projects:environments:metrics':
- import('./pages/projects/environments/metrics')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:merge_requests:index':
- case 'projects:issues:index':
- case 'projects:issues:show':
- shortcut_handler = true;
- break;
- case 'projects:milestones:index':
- import('./pages/projects/milestones/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:milestones:show':
- import('./pages/projects/milestones/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:milestones:show':
- import('./pages/groups/milestones/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'dashboard:milestones:show':
- import('./pages/dashboard/milestones/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'dashboard:issues':
- import('./pages/dashboard/issues')
- .then(callDefault)
- .catch(fail);
- break;
- case 'dashboard:merge_requests':
- import('./pages/dashboard/merge_requests')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:issues':
- import('./pages/groups/issues')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:merge_requests':
- import('./pages/groups/merge_requests')
- .then(callDefault)
- .catch(fail);
- break;
- case 'dashboard:todos:index':
- import('./pages/dashboard/todos/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:jobs:index':
- import('./pages/admin/jobs/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:projects:index':
- import('./pages/admin/projects/index/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:users:index':
- import('./pages/admin/users/shared')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:users:show':
- import('./pages/admin/users/shared')
- .then(callDefault)
- .catch(fail);
- break;
- case 'dashboard:projects:index':
- case 'dashboard:projects:starred':
- import('./pages/dashboard/projects')
- .then(callDefault)
- .catch(fail);
- break;
- case 'explore:projects:index':
- case 'explore:projects:trending':
- case 'explore:projects:starred':
- import('./pages/explore/projects')
- .then(callDefault)
- .catch(fail);
- break;
- case 'explore:groups:index':
- import('./pages/explore/groups')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:milestones:new':
- case 'projects:milestones:create':
- import('./pages/projects/milestones/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:milestones:edit':
- case 'projects:milestones:update':
- import('./pages/projects/milestones/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:milestones:new':
- case 'groups:milestones:create':
- import('./pages/groups/milestones/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:milestones:edit':
- case 'groups:milestones:update':
- import('./pages/groups/milestones/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:compare:show':
- import('./pages/projects/compare/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:branches:new':
- import('./pages/projects/branches/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:branches:create':
- import('./pages/projects/branches/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:branches:index':
- import('./pages/projects/branches/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:issues:new':
- import('./pages/projects/issues/new')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:issues:edit':
- import('./pages/projects/issues/edit')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:merge_requests:creations:new':
- import('./pages/projects/merge_requests/creations/new')
- .then(callDefault)
- .catch(fail);
- case 'projects:merge_requests:creations:diffs':
- import('./pages/projects/merge_requests/creations/diffs')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:merge_requests:edit':
- import('./pages/projects/merge_requests/edit')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:tags:new':
- import('./pages/projects/tags/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:snippets:show':
- import('./pages/projects/snippets/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:snippets:new':
- case 'projects:snippets:create':
- import('./pages/projects/snippets/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:snippets:edit':
- case 'projects:snippets:update':
- import('./pages/projects/snippets/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'snippets:new':
- import('./pages/snippets/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'snippets:edit':
- import('./pages/snippets/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'snippets:create':
- import('./pages/snippets/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'snippets:update':
- import('./pages/snippets/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:releases:edit':
- import('./pages/projects/releases/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:merge_requests:show':
- new Diff();
- new ZenMode();
+function initSearch() {
+ // Only when search form is present
+ if ($('.search').length) {
+ return new SearchAutocomplete();
+ }
+}
- initIssuableSidebar();
- initNotes();
+function initFieldErrors() {
+ $('.gl-show-field-errors').each((i, form) => {
+ new GlFieldErrors(form);
+ });
+}
- const mrShowNode = document.querySelector('.merge-request');
- window.mergeRequest = new MergeRequest({
- action: mrShowNode.dataset.mrAction,
- });
- shortcut_handler = new ShortcutsIssuable(true);
- break;
- case 'dashboard:activity':
- import('./pages/dashboard/activity')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:commit:show':
- import('./pages/projects/commit/show')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:commit:pipelines':
- import('./pages/projects/commit/pipelines')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:activity':
- import('./pages/projects/activity')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:commits:show':
- import('./pages/projects/commits/show')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:show':
- shortcut_handler = true;
- break;
- case 'projects:edit':
- import('./pages/projects/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:imports:show':
- import('./pages/projects/imports/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:pipelines:new':
- case 'projects:pipelines:create':
- import('./pages/projects/pipelines/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:pipelines:builds':
- case 'projects:pipelines:failures':
- case 'projects:pipelines:show':
- import('./pages/projects/pipelines/builds')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:activity':
- import('./pages/groups/activity')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:show':
- shortcut_handler = true;
- break;
- case 'groups:group_members:index':
- import('./pages/groups/group_members/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:project_members:index':
- import('./pages/projects/project_members')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:create':
- case 'groups:new':
- import('./pages/groups/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:edit':
- import('./pages/groups/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:groups:create':
- case 'admin:groups:new':
- import('./pages/admin/groups/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:groups:edit':
- import('./pages/admin/groups/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:tree:show':
- import('./pages/projects/tree/show')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:find_file:show':
- import('./pages/projects/find_file/show')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:blob:show':
- import('./pages/projects/blob/show')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:blame:show':
- import('./pages/projects/blame/show')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'groups:labels:new':
- import('./pages/groups/labels/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:labels:edit':
- import('./pages/groups/labels/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:labels:new':
- import('./pages/projects/labels/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:labels:edit':
- import('./pages/projects/labels/edit')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:labels:index':
- import('./pages/groups/labels/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:labels:index':
- import('./pages/projects/labels/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:network:show':
- // Ensure we don't create a particular shortcut handler here. This is
- // already created, where the network graph is created.
- shortcut_handler = true;
- break;
- case 'projects:forks:new':
- import('./pages/projects/forks/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:artifacts:browse':
- import('./pages/projects/artifacts/browse')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'projects:artifacts:file':
- import('./pages/projects/artifacts/file')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- case 'help:index':
- import('./pages/help')
- .then(callDefault)
- .catch(fail);
- break;
- case 'search:show':
- import('./pages/search/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:settings:repository:show':
- import('./pages/projects/settings/repository/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:settings:ci_cd:show':
- import('./pages/projects/settings/ci_cd/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups:settings:ci_cd:show':
- import('./pages/groups/settings/ci_cd/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'ci:lints:create':
- case 'ci:lints:show':
- import('./pages/ci/lints')
- .then(callDefault)
- .catch(fail);
- break;
- case 'users:show':
- import('./pages/users/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:conversational_development_index:show':
- import('./pages/admin/conversational_development_index/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'snippets:show':
- import('./pages/snippets/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'import:fogbugz:new_user_map':
- import('./pages/import/fogbugz/new_user_map')
- .then(callDefault)
- .catch(fail);
- break;
- case 'profiles:personal_access_tokens:index':
- import('./pages/profiles/personal_access_tokens')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin:impersonation_tokens:index':
- import('./pages/admin/impersonation_tokens')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:clusters:show':
- case 'projects:clusters:update':
- case 'projects:clusters:destroy':
- import('./pages/projects/clusters/show')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects:clusters:index':
- import('./pages/projects/clusters/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'dashboard:groups:index':
- import('./pages/dashboard/groups/index')
- .then(callDefault)
- .catch(fail);
- break;
- }
- switch (path[0]) {
- case 'sessions':
- import('./pages/sessions')
- .then(callDefault)
- .catch(fail);
- break;
- case 'omniauth_callbacks':
- import('./pages/omniauth_callbacks')
- .then(callDefault)
- .catch(fail);
- break;
- case 'admin':
- import('./pages/admin')
- .then(callDefault)
- .catch(fail);
- switch (path[1]) {
- case 'broadcast_messages':
- import('./pages/admin/broadcast_messages')
- .then(callDefault)
- .catch(fail);
- break;
- case 'cohorts':
- import('./pages/admin/cohorts')
- .then(callDefault)
- .catch(fail);
- break;
- case 'groups':
- switch (path[2]) {
- case 'show':
- import('./pages/admin/groups/show')
- .then(callDefault)
- .catch(fail);
- break;
- }
- break;
- case 'projects':
- import('./pages/admin/projects')
- .then(callDefault)
- .catch(fail);
- break;
- case 'labels':
- switch (path[2]) {
- case 'new':
- import('./pages/admin/labels/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'edit':
- import('./pages/admin/labels/edit')
- .then(callDefault)
- .catch(fail);
- break;
- }
- case 'abuse_reports':
- import('./pages/admin/abuse_reports')
- .then(callDefault)
- .catch(fail);
- break;
- }
- break;
- case 'profiles':
- import('./pages/profiles/index')
- .then(callDefault)
- .catch(fail);
- break;
- case 'projects':
- import('./pages/projects')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- switch (path[1]) {
- case 'compare':
- import('./pages/projects/compare')
- .then(callDefault)
- .catch(fail);
- break;
- case 'create':
- case 'new':
- import('./pages/projects/new')
- .then(callDefault)
- .catch(fail);
- break;
- case 'wikis':
- import('./pages/projects/wikis')
- .then(callDefault)
- .catch(fail);
- shortcut_handler = true;
- break;
- }
- break;
- }
- // If we haven't installed a custom shortcut handler, install the default one
- if (!shortcut_handler) {
- new Shortcuts();
- }
+function initPageShortcuts(page) {
+ const pagesWithCustomShortcuts = [
+ 'projects:activity',
+ 'projects:artifacts:browse',
+ 'projects:artifacts:file',
+ 'projects:blame:show',
+ 'projects:blob:show',
+ 'projects:commit:show',
+ 'projects:commits:show',
+ 'projects:find_file:show',
+ 'projects:issues:edit',
+ 'projects:issues:index',
+ 'projects:issues:new',
+ 'projects:issues:show',
+ 'projects:merge_requests:creations:diffs',
+ 'projects:merge_requests:creations:new',
+ 'projects:merge_requests:edit',
+ 'projects:merge_requests:index',
+ 'projects:merge_requests:show',
+ 'projects:network:show',
+ 'projects:show',
+ 'projects:tree:show',
+ 'groups:show',
+ ];
- if (document.querySelector('#peek')) {
- import('./performance_bar')
- .then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap
- .catch(fail);
- }
- };
+ if (pagesWithCustomShortcuts.indexOf(page) === -1) {
+ new Shortcuts();
+ }
+}
- Dispatcher.prototype.initSearch = function() {
- // Only when search form is present
- if ($('.search').length) {
- return new SearchAutocomplete();
- }
- };
+function initGFMInput() {
+ $('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
+ const gfm = new GfmAutoComplete(gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources);
+ const enableGFM = convertPermissionToBoolean(el.dataset.supportsAutocomplete);
+ gfm.setup($(el), {
+ emojis: true,
+ members: enableGFM,
+ issues: enableGFM,
+ milestones: enableGFM,
+ mergeRequests: enableGFM,
+ labels: enableGFM,
+ });
+ });
+}
- Dispatcher.prototype.initFieldErrors = function() {
- $('.gl-show-field-errors').each((i, form) => {
- new GlFieldErrors(form);
- });
- };
+function initPerformanceBar() {
+ if (document.querySelector('#peek')) {
+ import('./performance_bar')
+ .then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap
+ .catch(() => Flash('Error loading performance bar module'));
+ }
+}
- return Dispatcher;
- })();
-})();
+export default () => {
+ initSearch();
+ initFieldErrors();
-export default function initDispatcher() {
- return new Dispatcher();
-}
+ const page = $('body').attr('data-page');
+ if (page) {
+ initPageShortcuts(page);
+ initGFMInput();
+ initPerformanceBar();
+ }
+};
diff --git a/app/assets/javascripts/docs/docs_bundle.js b/app/assets/javascripts/docs/docs_bundle.js
index a32bd6d0fc7..897439f56b0 100644
--- a/app/assets/javascripts/docs/docs_bundle.js
+++ b/app/assets/javascripts/docs/docs_bundle.js
@@ -4,10 +4,7 @@ function addMousetrapClick(el, key) {
el.addEventListener('click', () => Mousetrap.trigger(key));
}
-function domContentLoaded() {
+export default () => {
addMousetrapClick(document.querySelector('.js-trigger-shortcut'), '?');
addMousetrapClick(document.querySelector('.js-trigger-search-bar'), 's');
-}
-
-document.addEventListener('DOMContentLoaded', domContentLoaded);
-
+};
diff --git a/app/assets/javascripts/droplab/constants.js b/app/assets/javascripts/droplab/constants.js
index 673e9bb4c0f..868d47e91b3 100644
--- a/app/assets/javascripts/droplab/constants.js
+++ b/app/assets/javascripts/droplab/constants.js
@@ -3,7 +3,6 @@ const DATA_DROPDOWN = 'data-dropdown';
const SELECTED_CLASS = 'droplab-item-selected';
const ACTIVE_CLASS = 'droplab-item-active';
const IGNORE_CLASS = 'droplab-item-ignore';
-const IGNORE_HIDING_CLASS = 'droplab-item-ignore-hiding';
// Matches `{{anything}}` and `{{ everything }}`.
const TEMPLATE_REGEX = /\{\{(.+?)\}\}/g;
@@ -14,5 +13,4 @@ export {
ACTIVE_CLASS,
TEMPLATE_REGEX,
IGNORE_CLASS,
- IGNORE_HIDING_CLASS,
};
diff --git a/app/assets/javascripts/droplab/drop_down.js b/app/assets/javascripts/droplab/drop_down.js
index 5eb0a339a1c..3cc316c3f3e 100644
--- a/app/assets/javascripts/droplab/drop_down.js
+++ b/app/assets/javascripts/droplab/drop_down.js
@@ -1,13 +1,14 @@
import utils from './utils';
-import { SELECTED_CLASS, IGNORE_CLASS, IGNORE_HIDING_CLASS } from './constants';
+import { SELECTED_CLASS, IGNORE_CLASS } from './constants';
class DropDown {
- constructor(list, config = {}) {
+ constructor(list, config = { }) {
this.currentIndex = 0;
this.hidden = true;
this.list = typeof list === 'string' ? document.querySelector(list) : list;
this.items = [];
this.eventWrapper = {};
+ this.hideOnClick = config.hideOnClick !== false;
if (config.addActiveClassToDropdownButton) {
this.dropdownToggle = this.list.parentNode.querySelector('.js-dropdown-toggle');
@@ -37,15 +38,17 @@ class DropDown {
clickEvent(e) {
if (e.target.tagName === 'UL') return;
- if (e.target.classList.contains(IGNORE_CLASS)) return;
+ if (e.target.closest(`.${IGNORE_CLASS}`)) return;
- const selected = utils.closest(e.target, 'LI');
+ const selected = e.target.closest('li');
if (!selected) return;
this.addSelectedClass(selected);
e.preventDefault();
- if (!e.target.classList.contains(IGNORE_HIDING_CLASS)) this.hide();
+ if (this.hideOnClick) {
+ this.hide();
+ }
const listEvent = new CustomEvent('click.dl', {
detail: {
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index bd4c58b7cb1..417258e0092 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -17,9 +17,9 @@ class DueDateSelect {
this.$value = $block.find('.value');
this.$valueContent = $block.find('.value-content');
this.$sidebarValue = $('.js-due-date-sidebar-value', $block);
- this.fieldName = $dropdown.data('field-name');
- this.abilityName = $dropdown.data('ability-name');
- this.issueUpdateURL = $dropdown.data('issue-update');
+ this.fieldName = $dropdown.data('fieldName');
+ this.abilityName = $dropdown.data('abilityName');
+ this.issueUpdateURL = $dropdown.data('issueUpdate');
this.rawSelectedDate = null;
this.displayedDate = null;
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index a9d554e549e..79326ca3487 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -1,8 +1,9 @@
<script>
import Timeago from 'timeago.js';
import _ from 'underscore';
- import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
- import { humanize } from '../../lib/utils/text_utility';
+ import tooltip from '~/vue_shared/directives/tooltip';
+ import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
+ import { humanize } from '~/lib/utils/text_utility';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue';
@@ -21,14 +22,18 @@
export default {
components: {
- userAvatarLink,
- 'commit-component': CommitComponent,
- 'actions-component': ActionsComponent,
- 'external-url-component': ExternalUrlComponent,
- 'stop-component': StopComponent,
- 'rollback-component': RollbackComponent,
- 'terminal-button-component': TerminalButtonComponent,
- 'monitoring-button-component': MonitoringButtonComponent,
+ UserAvatarLink,
+ CommitComponent,
+ ActionsComponent,
+ ExternalUrlComponent,
+ StopComponent,
+ RollbackComponent,
+ TerminalButtonComponent,
+ MonitoringButtonComponent,
+ },
+
+ directives: {
+ tooltip,
},
props: {
@@ -443,7 +448,11 @@
v-if="!model.isFolder"
class="environment-name flex-truncate-parent table-mobile-content"
:href="environmentPath">
- <span class="flex-truncate-child">{{ model.name }}</span>
+ <span
+ class="flex-truncate-child"
+ v-tooltip
+ :title="model.name"
+ >{{ model.name }}</span>
</a>
<span
v-else
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index b4eca47957e..22863e926d4 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -2,8 +2,8 @@
/**
* Render environments table.
*/
+import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import environmentItem from './environment_item.vue';
-import loadingIcon from '../../vue_shared/components/loading_icon.vue';
export default {
components: {
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 5d2d14c7682..de0fbdb2e91 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -5,7 +5,7 @@ import Translate from '../../vue_shared/translate';
Vue.use(Translate);
-document.addEventListener('DOMContentLoaded', () => new Vue({
+export default () => new Vue({
el: '#environments-folder-list-view',
components: {
environmentsFolderApp,
@@ -32,4 +32,4 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
},
});
},
-}));
+});
diff --git a/app/assets/javascripts/files_comment_button.js b/app/assets/javascripts/files_comment_button.js
index 90020344748..6a4874e1ab8 100644
--- a/app/assets/javascripts/files_comment_button.js
+++ b/app/assets/javascripts/files_comment_button.js
@@ -25,7 +25,7 @@ export default {
if (!this.userCanCreateNote) {
// data-can-create-note is an empty string when true, otherwise undefined
- this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('can-create-note') === '';
+ this.userCanCreateNote = $diffFile.closest(DIFF_CONTAINER_SELECTOR).data('canCreateNote') === '';
}
this.isParallelView = Cookies.get('diff_view') === 'parallel';
diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js
deleted file mode 100644
index c51d4b056af..00000000000
--- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import eventHub from '../event_hub';
-
-export default {
- name: 'RecentSearchesDropdownContent',
-
- props: {
- items: {
- type: Array,
- required: true,
- },
- isLocalStorageAvailable: {
- type: Boolean,
- required: false,
- default: true,
- },
- allowedKeys: {
- type: Array,
- required: true,
- },
- },
-
- computed: {
- processedItems() {
- return this.items.map((item) => {
- const { tokens, searchToken }
- = gl.FilteredSearchTokenizer.processTokens(item, this.allowedKeys);
-
- const resultantTokens = tokens.map(token => ({
- prefix: `${token.key}:`,
- suffix: `${token.symbol}${token.value}`,
- }));
-
- return {
- text: item,
- tokens: resultantTokens,
- searchToken,
- };
- });
- },
- hasItems() {
- return this.items.length > 0;
- },
- },
-
- methods: {
- onItemActivated(text) {
- eventHub.$emit('recentSearchesItemSelected', text);
- },
- onRequestClearRecentSearches(e) {
- // Stop the dropdown from closing
- e.stopPropagation();
-
- eventHub.$emit('requestClearRecentSearches');
- },
- },
-
- template: `
- <div>
- <div
- v-if="!isLocalStorageAvailable"
- class="dropdown-info-note">
- This feature requires local storage to be enabled
- </div>
- <ul v-else-if="hasItems">
- <li
- v-for="(item, index) in processedItems"
- :key="index">
- <button
- type="button"
- class="filtered-search-history-dropdown-item"
- @click="onItemActivated(item.text)">
- <span>
- <span
- v-for="(token, tokenIndex) in item.tokens"
- class="filtered-search-history-dropdown-token">
- <span class="name">{{ token.prefix }}</span><span class="value">{{ token.suffix }}</span>
- </span>
- </span>
- <span class="filtered-search-history-dropdown-search-token">
- {{ item.searchToken }}
- </span>
- </button>
- </li>
- <li class="divider"></li>
- <li>
- <button
- type="button"
- class="filtered-search-history-clear-button"
- @click="onRequestClearRecentSearches($event)">
- Clear recent searches
- </button>
- </li>
- </ul>
- <div
- v-else
- class="dropdown-info-note">
- You don't have any recent searches
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
new file mode 100644
index 00000000000..26618af9515
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
@@ -0,0 +1,104 @@
+<script>
+import eventHub from '../event_hub';
+import FilteredSearchTokenizer from '../filtered_search_tokenizer';
+
+export default {
+ name: 'RecentSearchesDropdownContent',
+ props: {
+ items: {
+ type: Array,
+ required: true,
+ },
+ isLocalStorageAvailable: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ allowedKeys: {
+ type: Array,
+ required: true,
+ },
+ },
+ computed: {
+ processedItems() {
+ return this.items.map((item) => {
+ const { tokens, searchToken }
+ = FilteredSearchTokenizer.processTokens(item, this.allowedKeys);
+
+ const resultantTokens = tokens.map(token => ({
+ prefix: `${token.key}:`,
+ suffix: `${token.symbol}${token.value}`,
+ }));
+
+ return {
+ text: item,
+ tokens: resultantTokens,
+ searchToken,
+ };
+ });
+ },
+ hasItems() {
+ return this.items.length > 0;
+ },
+ },
+ methods: {
+ onItemActivated(text) {
+ eventHub.$emit('recentSearchesItemSelected', text);
+ },
+ onRequestClearRecentSearches(e) {
+ // Stop the dropdown from closing
+ e.stopPropagation();
+
+ eventHub.$emit('requestClearRecentSearches');
+ },
+ },
+};
+</script>
+<template>
+ <div>
+ <div
+ v-if="!isLocalStorageAvailable"
+ class="dropdown-info-note">
+ This feature requires local storage to be enabled
+ </div>
+ <ul v-else-if="hasItems">
+ <li
+ v-for="(item, index) in processedItems"
+ :key="`processed-items-${index}`"
+ >
+ <button
+ type="button"
+ class="filtered-search-history-dropdown-item"
+ @click="onItemActivated(item.text)">
+ <span>
+ <span
+ class="filtered-search-history-dropdown-token"
+ v-for="(token, index) in item.tokens"
+ :key="`dropdown-token-${index}`"
+ >
+ <span class="name">{{ token.prefix }}</span>
+ <span class="value">{{ token.suffix }}</span>
+ </span>
+ </span>
+ <span class="filtered-search-history-dropdown-search-token">
+ {{ item.searchToken }}
+ </span>
+ </button>
+ </li>
+ <li class="divider"></li>
+ <li>
+ <button
+ type="button"
+ class="filtered-search-history-clear-button"
+ @click="onRequestClearRecentSearches($event)">
+ Clear recent searches
+ </button>
+ </li>
+ </ul>
+ <div
+ v-else
+ class="dropdown-info-note">
+ You don't have any recent searches
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/filtered_search/dropdown_emoji.js b/app/assets/javascripts/filtered_search/dropdown_emoji.js
index a6cc079d720..5ddd0e5e690 100644
--- a/app/assets/javascripts/filtered_search/dropdown_emoji.js
+++ b/app/assets/javascripts/filtered_search/dropdown_emoji.js
@@ -1,9 +1,10 @@
import Flash from '../flash';
import Ajax from '../droplab/plugins/ajax';
import Filter from '../droplab/plugins/filter';
-import './filtered_search_dropdown';
+import FilteredSearchDropdown from './filtered_search_dropdown';
+import DropdownUtils from './dropdown_utils';
-class DropdownEmoji extends gl.FilteredSearchDropdown {
+export default class DropdownEmoji extends FilteredSearchDropdown {
constructor(options = {}) {
super(options);
this.config = {
@@ -49,7 +50,7 @@ class DropdownEmoji extends gl.FilteredSearchDropdown {
itemClicked(e) {
super.itemClicked(e, (selected) => {
const name = selected.querySelector('.js-data-value').innerText.trim();
- return gl.DropdownUtils.getEscapedText(name);
+ return DropdownUtils.getEscapedText(name);
});
}
@@ -76,6 +77,3 @@ class DropdownEmoji extends gl.FilteredSearchDropdown {
.addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
}
}
-
-window.gl = window.gl || {};
-gl.DropdownEmoji = DropdownEmoji;
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 23040cd9eb8..184b34b7b5e 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -1,14 +1,17 @@
import Filter from '~/droplab/plugins/filter';
-import './filtered_search_dropdown';
+import FilteredSearchDropdown from './filtered_search_dropdown';
+import DropdownUtils from './dropdown_utils';
+import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
+import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
-class DropdownHint extends gl.FilteredSearchDropdown {
+export default class DropdownHint extends FilteredSearchDropdown {
constructor(options = {}) {
const { input, tokenKeys } = options;
super(options);
this.config = {
Filter: {
template: 'hint',
- filterFunction: gl.DropdownUtils.filterHint.bind(null, {
+ filterFunction: DropdownUtils.filterHint.bind(null, {
input,
allowedKeys: tokenKeys.getKeys(),
}),
@@ -45,10 +48,10 @@ class DropdownHint extends gl.FilteredSearchDropdown {
});
if (searchTerms.length > 0) {
- gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
+ FilteredSearchVisualTokens.addSearchVisualToken(searchTerms.join(' '));
}
- gl.FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
+ FilteredSearchDropdownManager.addWordToInput(token.replace(':', ''), '', false, this.container);
}
this.dismissDropdown();
this.dispatchInputEvent();
@@ -73,6 +76,3 @@ class DropdownHint extends gl.FilteredSearchDropdown {
this.droplab.addHook(this.input, this.dropdown, [Filter], this.config).init();
}
}
-
-window.gl = window.gl || {};
-gl.DropdownHint = DropdownHint;
diff --git a/app/assets/javascripts/filtered_search/dropdown_non_user.js b/app/assets/javascripts/filtered_search/dropdown_non_user.js
index 788fb1dc614..2ffda7e2037 100644
--- a/app/assets/javascripts/filtered_search/dropdown_non_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_non_user.js
@@ -1,9 +1,10 @@
import Flash from '../flash';
import Ajax from '../droplab/plugins/ajax';
import Filter from '../droplab/plugins/filter';
-import './filtered_search_dropdown';
+import FilteredSearchDropdown from './filtered_search_dropdown';
+import DropdownUtils from './dropdown_utils';
-class DropdownNonUser extends gl.FilteredSearchDropdown {
+export default class DropdownNonUser extends FilteredSearchDropdown {
constructor(options = {}) {
const { input, endpoint, symbol, preprocessing } = options;
super(options);
@@ -21,7 +22,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
},
},
Filter: {
- filterFunction: gl.DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
+ filterFunction: DropdownUtils.filterWithSymbol.bind(null, this.symbol, input),
template: 'title',
},
};
@@ -30,7 +31,7 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
itemClicked(e) {
super.itemClicked(e, (selected) => {
const title = selected.querySelector('.js-data-value').innerText.trim();
- return `${this.symbol}${gl.DropdownUtils.getEscapedText(title)}`;
+ return `${this.symbol}${DropdownUtils.getEscapedText(title)}`;
});
}
@@ -45,6 +46,3 @@ class DropdownNonUser extends gl.FilteredSearchDropdown {
.addHook(this.input, this.dropdown, [Ajax, Filter], this.config).init();
}
}
-
-window.gl = window.gl || {};
-gl.DropdownNonUser = DropdownNonUser;
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index a9e2b65def0..d36f38a70b5 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -1,9 +1,11 @@
import Flash from '../flash';
import AjaxFilter from '../droplab/plugins/ajax_filter';
-import './filtered_search_dropdown';
+import FilteredSearchDropdown from './filtered_search_dropdown';
import { addClassIfElementExists } from '../lib/utils/dom_utils';
+import DropdownUtils from './dropdown_utils';
+import FilteredSearchTokenizer from './filtered_search_tokenizer';
-class DropdownUser extends gl.FilteredSearchDropdown {
+export default class DropdownUser extends FilteredSearchDropdown {
constructor(options = {}) {
const { tokenKeys } = options;
super(options);
@@ -12,7 +14,6 @@ class DropdownUser extends gl.FilteredSearchDropdown {
endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
searchKey: 'search',
params: {
- per_page: 20,
active: true,
group_id: this.getGroupId(),
project_id: this.getProjectId(),
@@ -56,8 +57,8 @@ class DropdownUser extends gl.FilteredSearchDropdown {
}
getSearchInput() {
- const query = gl.DropdownUtils.getSearchInput(this.input);
- const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get());
+ const query = DropdownUtils.getSearchInput(this.input);
+ const { lastToken } = FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get());
let value = lastToken || '';
@@ -78,6 +79,3 @@ class DropdownUser extends gl.FilteredSearchDropdown {
this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init();
}
}
-
-window.gl = window.gl || {};
-gl.DropdownUser = DropdownUser;
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index cf8a9b0402b..9bc36c1f9b6 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -1,7 +1,10 @@
import _ from 'underscore';
import FilteredSearchContainer from './container';
+import FilteredSearchTokenizer from './filtered_search_tokenizer';
+import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
+import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
-class DropdownUtils {
+export default class DropdownUtils {
static getEscapedText(text) {
let escapedText = text;
const hasSpace = text.indexOf(' ') !== -1;
@@ -24,7 +27,7 @@ class DropdownUtils {
static filterWithSymbol(filterSymbol, input, item) {
const updatedItem = item;
- const searchInput = gl.DropdownUtils.getSearchInput(input);
+ const searchInput = DropdownUtils.getSearchInput(input);
const title = updatedItem.title.toLowerCase();
let value = searchInput.toLowerCase();
@@ -114,9 +117,9 @@ class DropdownUtils {
static filterHint(config, item) {
const { input, allowedKeys } = config;
const updatedItem = item;
- const searchInput = gl.DropdownUtils.getSearchQuery(input);
+ const searchInput = DropdownUtils.getSearchQuery(input);
const { lastToken, tokens } =
- gl.FilteredSearchTokenizer.processTokens(searchInput, allowedKeys);
+ FilteredSearchTokenizer.processTokens(searchInput, allowedKeys);
const lastKey = lastToken.key || lastToken || '';
const allowMultiple = item.type === 'array';
const itemInExistingTokens = tokens.some(t => t.key === item.hint);
@@ -140,7 +143,7 @@ class DropdownUtils {
const dataValue = selected.getAttribute('data-value');
if (dataValue) {
- gl.FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true);
+ FilteredSearchDropdownManager.addWordToInput(filter, dataValue, true);
}
// Return boolean based on whether it was set
@@ -190,7 +193,7 @@ class DropdownUtils {
}
} else if (token.classList.contains('input-token')) {
const { isLastVisualTokenValid } =
- gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
const inputValue = input && input.value;
@@ -211,7 +214,7 @@ class DropdownUtils {
static getSearchInput(filteredSearchInput) {
const inputValue = filteredSearchInput.value;
- const { right } = gl.DropdownUtils.getInputSelectionPosition(filteredSearchInput);
+ const { right } = DropdownUtils.getInputSelectionPosition(filteredSearchInput);
return inputValue.slice(0, right);
}
@@ -252,6 +255,3 @@ class DropdownUtils {
};
}
}
-
-window.gl = window.gl || {};
-gl.DropdownUtils = DropdownUtils;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
index 9e9a9ef74be..fb4ae1d17dd 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown.js
@@ -1,6 +1,9 @@
+import DropdownUtils from './dropdown_utils';
+import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
+
const DATA_DROPDOWN_TRIGGER = 'data-dropdown-trigger';
-class FilteredSearchDropdown {
+export default class FilteredSearchDropdown {
constructor({ droplab, dropdown, input, filter }) {
this.droplab = droplab;
this.hookId = input && input.id;
@@ -30,11 +33,11 @@ class FilteredSearchDropdown {
const { selected } = e.detail;
if (selected.tagName === 'LI' && selected.innerHTML) {
- const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(this.filter, selected);
+ const dataValueSet = DropdownUtils.setDataValueIfSelected(this.filter, selected);
if (!dataValueSet) {
const value = getValueFunction(selected);
- gl.FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
+ FilteredSearchDropdownManager.addWordToInput(this.filter, value, true);
}
this.resetFilters();
@@ -108,6 +111,9 @@ class FilteredSearchDropdown {
if (hook) {
const data = hook.list.data || [];
+
+ if (!data) return;
+
const results = data.map((o) => {
const updated = o;
updated.droplab_hidden = false;
@@ -117,6 +123,3 @@ class FilteredSearchDropdown {
}
}
}
-
-window.gl = window.gl || {};
-gl.FilteredSearchDropdown = FilteredSearchDropdown;
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 b2add862051..c64553a1b92 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,20 @@
import _ from 'underscore';
import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
-
-class FilteredSearchDropdownManager {
+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 FilteredSearchVisualTokens from './filtered_search_visual_tokens';
+
+export default class FilteredSearchDropdownManager {
constructor(baseEndpoint = '', tokenizer, page, isGroup, filteredSearchTokenKeys) {
this.container = FilteredSearchContainer.container;
this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = tokenizer;
- this.filteredSearchTokenKeys = filteredSearchTokenKeys;
+ this.filteredSearchTokenKeys = filteredSearchTokenKeys || FilteredSearchTokenKeys;
this.filteredSearchInput = this.container.querySelector('.filtered-search');
this.page = page;
@@ -33,24 +40,24 @@ class FilteredSearchDropdownManager {
const allowedMappings = {
hint: {
reference: null,
- gl: 'DropdownHint',
+ gl: DropdownHint,
element: this.container.querySelector('#js-dropdown-hint'),
},
};
const availableMappings = {
author: {
reference: null,
- gl: 'DropdownUser',
+ gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-author'),
},
assignee: {
reference: null,
- gl: 'DropdownUser',
+ gl: DropdownUser,
element: this.container.querySelector('#js-dropdown-assignee'),
},
milestone: {
reference: null,
- gl: 'DropdownNonUser',
+ gl: DropdownNonUser,
extraArguments: {
endpoint: `${this.baseEndpoint}/milestones.json`,
symbol: '%',
@@ -59,17 +66,17 @@ class FilteredSearchDropdownManager {
},
label: {
reference: null,
- gl: 'DropdownNonUser',
+ gl: DropdownNonUser,
extraArguments: {
endpoint: `${this.baseEndpoint}/labels.json`,
symbol: '~',
- preprocessing: gl.DropdownUtils.duplicateLabelPreprocessing,
+ preprocessing: DropdownUtils.duplicateLabelPreprocessing,
},
element: this.container.querySelector('#js-dropdown-label'),
},
'my-reaction': {
reference: null,
- gl: 'DropdownEmoji',
+ gl: DropdownEmoji,
element: this.container.querySelector('#js-dropdown-my-reaction'),
},
};
@@ -86,11 +93,11 @@ class FilteredSearchDropdownManager {
static addWordToInput(tokenName, tokenValue = '', clicked = false) {
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
- gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, tokenValue);
input.value = '';
if (clicked) {
- gl.FilteredSearchVisualTokens.moveInputToTheRight();
+ FilteredSearchVisualTokens.moveInputToTheRight();
}
}
@@ -131,9 +138,9 @@ class FilteredSearchDropdownManager {
const extraArguments = mappingKey.extraArguments || {};
const glArguments = Object.assign({}, defaultArguments, extraArguments);
- // Passing glArguments to `new gl[glClass](<arguments>)`
+ // Passing glArguments to `new glClass(<arguments>)`
mappingKey.reference =
- new (Function.prototype.bind.apply(gl[glClass], [null, glArguments]))();
+ new (Function.prototype.bind.apply(glClass, [null, glArguments]))();
}
if (firstLoad) {
@@ -171,7 +178,7 @@ class FilteredSearchDropdownManager {
}
setDropdown() {
- const query = gl.DropdownUtils.getSearchQuery(true);
+ const query = DropdownUtils.getSearchQuery(true);
const { lastToken, searchToken } =
this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys());
@@ -216,6 +223,3 @@ class FilteredSearchDropdownManager {
this.droplab.destroy();
}
}
-
-window.gl = window.gl || {};
-gl.FilteredSearchDropdownManager = FilteredSearchDropdownManager;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 532a5fe1090..e294b629bd0 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -1,15 +1,23 @@
import _ from 'underscore';
+import {
+ getParameterByName,
+ getUrlParamsArray,
+} from '~/lib/utils/common_utils';
import { visitUrl } from '../lib/utils/url_utility';
import Flash from '../flash';
import FilteredSearchContainer from './container';
-import RecentSearchesRoot from './recent_searches_root';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
+import RecentSearchesRoot from './recent_searches_root';
import RecentSearchesStore from './stores/recent_searches_store';
import RecentSearchesService from './services/recent_searches_service';
import eventHub from './event_hub';
import { addClassIfElementExists } from '../lib/utils/dom_utils';
+import FilteredSearchTokenizer from './filtered_search_tokenizer';
+import FilteredSearchDropdownManager from './filtered_search_dropdown_manager';
+import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
+import DropdownUtils from './dropdown_utils';
-class FilteredSearchManager {
+export default class FilteredSearchManager {
constructor({
page,
filteredSearchTokenKeys = FilteredSearchTokenKeys,
@@ -66,8 +74,8 @@ class FilteredSearchManager {
});
if (this.filteredSearchInput) {
- this.tokenizer = gl.FilteredSearchTokenizer;
- this.dropdownManager = new gl.FilteredSearchDropdownManager(
+ this.tokenizer = FilteredSearchTokenizer;
+ this.dropdownManager = new FilteredSearchDropdownManager(
this.filteredSearchInput.getAttribute('data-base-endpoint') || '',
this.tokenizer,
this.page,
@@ -85,7 +93,6 @@ class FilteredSearchManager {
this.bindEvents();
this.loadSearchParamsFromURL();
this.dropdownManager.setDropdown();
-
this.cleanupWrapper = this.cleanup.bind(this);
document.addEventListener('beforeunload', this.cleanupWrapper);
}
@@ -197,8 +204,8 @@ class FilteredSearchManager {
// 8 = Backspace Key
// 46 = Delete Key
if (e.keyCode === 8 || e.keyCode === 46) {
- const { lastVisualToken } = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
- const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(lastVisualToken);
+ const { lastVisualToken } = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ const { tokenName, tokenValue } = DropdownUtils.getVisualTokenValues(lastVisualToken);
const canEdit = tokenName && this.canEdit && this.canEdit(tokenName, tokenValue);
if (this.filteredSearchInput.value === '' && lastVisualToken && canEdit) {
@@ -206,8 +213,8 @@ class FilteredSearchManager {
if (backspaceCount === 2) {
backspaceCount = 0;
- this.filteredSearchInput.value = gl.FilteredSearchVisualTokens.getLastTokenPartial();
- gl.FilteredSearchVisualTokens.removeLastTokenPartial();
+ this.filteredSearchInput.value = FilteredSearchVisualTokens.getLastTokenPartial();
+ FilteredSearchVisualTokens.removeLastTokenPartial();
}
}
@@ -275,7 +282,7 @@ class FilteredSearchManager {
e.stopImmediatePropagation();
const button = e.target.closest('.selectable');
- gl.FilteredSearchVisualTokens.selectToken(button, true);
+ FilteredSearchVisualTokens.selectToken(button, true);
this.removeSelectedToken();
}
}
@@ -287,7 +294,7 @@ class FilteredSearchManager {
const isElementTokensContainer = e.target.classList.contains('tokens-container');
if ((!isElementInFilteredSearch && !isElementInFilterDropdown) || isElementTokensContainer) {
- gl.FilteredSearchVisualTokens.moveInputToTheRight();
+ FilteredSearchVisualTokens.moveInputToTheRight();
this.dropdownManager.resetDropdowns();
}
}
@@ -300,13 +307,13 @@ class FilteredSearchManager {
if (token && canEdit) {
e.preventDefault();
e.stopPropagation();
- gl.FilteredSearchVisualTokens.editToken(token);
+ FilteredSearchVisualTokens.editToken(token);
this.tokenChange();
}
}
toggleClearSearchButton() {
- const query = gl.DropdownUtils.getSearchQuery();
+ const query = DropdownUtils.getSearchQuery();
const hidden = 'hidden';
const hasHidden = this.clearSearchButton.classList.contains(hidden);
@@ -318,7 +325,7 @@ class FilteredSearchManager {
}
handleInputPlaceholder() {
- const query = gl.DropdownUtils.getSearchQuery();
+ const query = DropdownUtils.getSearchQuery();
const placeholder = 'Search or filter results...';
const currentPlaceholder = this.filteredSearchInput.placeholder;
@@ -338,7 +345,7 @@ class FilteredSearchManager {
}
removeSelectedToken() {
- gl.FilteredSearchVisualTokens.removeSelectedToken();
+ FilteredSearchVisualTokens.removeSelectedToken();
this.handleInputPlaceholder();
this.toggleClearSearchButton();
this.dropdownManager.updateCurrentDropdownOffset();
@@ -358,7 +365,7 @@ class FilteredSearchManager {
let canClearToken = t.classList.contains('js-visual-token');
if (canClearToken) {
- const { tokenName, tokenValue } = gl.DropdownUtils.getVisualTokenValues(t);
+ const { tokenName, tokenValue } = DropdownUtils.getVisualTokenValues(t);
canClearToken = this.canEdit && this.canEdit(tokenName, tokenValue);
}
@@ -386,12 +393,12 @@ class FilteredSearchManager {
const { tokens, searchToken }
= this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys());
const { isLastVisualTokenValid }
- = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ = FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (isLastVisualTokenValid) {
tokens.forEach((t) => {
input.value = input.value.replace(`${t.key}:${t.symbol}${t.value}`, '');
- gl.FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`);
+ FilteredSearchVisualTokens.addFilterVisualToken(t.key, `${t.symbol}${t.value}`);
});
const fragments = searchToken.split(':');
@@ -404,10 +411,10 @@ class FilteredSearchManager {
const searchTerms = inputValues.join(' ');
input.value = input.value.replace(searchTerms, '');
- gl.FilteredSearchVisualTokens.addSearchVisualToken(searchTerms);
+ FilteredSearchVisualTokens.addSearchVisualToken(searchTerms);
}
- gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenKey);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenKey);
input.value = input.value.replace(`${tokenKey}:`, '');
}
} else {
@@ -415,7 +422,7 @@ class FilteredSearchManager {
const valueCompletedRegex = /([~%@]{0,1}".+")|([~%@]{0,1}'.+')|^((?![~%@]')(?![~%@]")(?!')(?!")).*/g;
if (searchToken.match(valueCompletedRegex) && input.value[input.value.length - 1] === ' ') {
- gl.FilteredSearchVisualTokens.addFilterVisualToken(searchToken);
+ FilteredSearchVisualTokens.addFilterVisualToken(searchToken);
// Trim the last space as seen in the if statement above
input.value = input.value.replace(searchToken, '').trim();
@@ -431,7 +438,7 @@ class FilteredSearchManager {
saveCurrentSearchQuery() {
// Don't save before we have fetched the already saved searches
this.fetchingRecentSearchesPromise.then(() => {
- const searchQuery = gl.DropdownUtils.getSearchQuery();
+ const searchQuery = DropdownUtils.getSearchQuery();
if (searchQuery.length > 0) {
const resultantSearches = this.recentSearchesStore.addRecentSearch(searchQuery);
this.recentSearchesService.save(resultantSearches);
@@ -447,7 +454,7 @@ class FilteredSearchManager {
}
loadSearchParamsFromURL() {
- const urlParams = gl.utils.getUrlParamsArray();
+ const urlParams = getUrlParamsArray();
const params = this.getAllParams(urlParams);
const usernameParams = this.getUsernameParams();
let hasFilteredSearch = false;
@@ -463,7 +470,7 @@ class FilteredSearchManager {
if (condition) {
hasFilteredSearch = true;
const canEdit = this.canEdit && this.canEdit(condition.tokenKey);
- gl.FilteredSearchVisualTokens.addFilterVisualToken(
+ FilteredSearchVisualTokens.addFilterVisualToken(
condition.tokenKey,
condition.value,
canEdit,
@@ -492,7 +499,7 @@ class FilteredSearchManager {
hasFilteredSearch = true;
const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue);
- gl.FilteredSearchVisualTokens.addFilterVisualToken(
+ FilteredSearchVisualTokens.addFilterVisualToken(
sanitizedKey,
`${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`,
canEdit,
@@ -503,7 +510,7 @@ class FilteredSearchManager {
hasFilteredSearch = true;
const tokenName = 'assignee';
const canEdit = this.canEdit && this.canEdit(tokenName);
- gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit);
}
} else if (!match && keyParam === 'author_id') {
const id = parseInt(value, 10);
@@ -511,7 +518,7 @@ class FilteredSearchManager {
hasFilteredSearch = true;
const tokenName = 'author';
const canEdit = this.canEdit && this.canEdit(tokenName);
- gl.FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit);
+ FilteredSearchVisualTokens.addFilterVisualToken(tokenName, `@${usernameParams[id]}`, canEdit);
}
} else if (!match && keyParam === 'search') {
hasFilteredSearch = true;
@@ -543,13 +550,13 @@ class FilteredSearchManager {
search(state = null) {
const paths = [];
- const searchQuery = gl.DropdownUtils.getSearchQuery();
+ const searchQuery = DropdownUtils.getSearchQuery();
this.saveCurrentSearchQuery();
const { tokens, searchToken }
= this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys());
- const currentState = state || gl.utils.getParameterByName('state') || 'opened';
+ const currentState = state || getParameterByName('state') || 'opened';
paths.push(`state=${currentState}`);
tokens.forEach((token) => {
@@ -628,6 +635,3 @@ class FilteredSearchManager {
return true;
}
}
-
-window.gl = window.gl || {};
-gl.FilteredSearchManager = FilteredSearchManager;
diff --git a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
index f2e66503e5e..d75610f6d68 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_tokenizer.js
@@ -1,6 +1,6 @@
import './filtered_search_token_keys';
-class FilteredSearchTokenizer {
+export default class FilteredSearchTokenizer {
static processTokens(input, allowedKeys) {
// Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single)
@@ -50,6 +50,3 @@ class FilteredSearchTokenizer {
};
}
}
-
-window.gl = window.gl || {};
-gl.FilteredSearchTokenizer = FilteredSearchTokenizer;
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 2e859d2de3a..a19bb882410 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -3,8 +3,9 @@ import AjaxCache from '../lib/utils/ajax_cache';
import Flash from '../flash';
import FilteredSearchContainer from './container';
import UsersCache from '../lib/utils/users_cache';
+import DropdownUtils from './dropdown_utils';
-class FilteredSearchVisualTokens {
+export default class FilteredSearchVisualTokens {
static getLastVisualTokenBeforeInput() {
const inputLi = FilteredSearchContainer.container.querySelector('.input-token');
const lastVisualToken = inputLi && inputLi.previousElementSibling;
@@ -74,7 +75,7 @@ class FilteredSearchVisualTokens {
let processed = labels;
if (!labels.preprocessed) {
- processed = gl.DropdownUtils.duplicateLabelPreprocessing(labels);
+ processed = DropdownUtils.duplicateLabelPreprocessing(labels);
AjaxCache.override(labelsEndpoint, processed);
processed.preprocessed = true;
}
@@ -90,7 +91,7 @@ class FilteredSearchVisualTokens {
return AjaxCache.retrieve(labelsEndpoint)
.then(FilteredSearchVisualTokens.preprocessLabel.bind(null, labelsEndpoint))
.then((labels) => {
- const matchingLabel = (labels || []).find(label => `~${gl.DropdownUtils.getEscapedText(label.title)}` === tokenValue);
+ const matchingLabel = (labels || []).find(label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue);
if (!matchingLabel) {
return;
@@ -259,11 +260,11 @@ class FilteredSearchVisualTokens {
static tokenizeInput() {
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
const { isLastVisualTokenValid } =
- gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (input.value) {
if (isLastVisualTokenValid) {
- gl.FilteredSearchVisualTokens.addSearchVisualToken(input.value);
+ FilteredSearchVisualTokens.addSearchVisualToken(input.value);
} else {
FilteredSearchVisualTokens.addValueToPreviousVisualTokenElement(input.value);
}
@@ -324,12 +325,12 @@ class FilteredSearchVisualTokens {
if (!tokenContainer.lastElementChild.isEqualNode(inputLi)) {
const { isLastVisualTokenValid } =
- gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
+ FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
if (!isLastVisualTokenValid) {
- const lastPartial = gl.FilteredSearchVisualTokens.getLastTokenPartial();
- gl.FilteredSearchVisualTokens.removeLastTokenPartial();
- gl.FilteredSearchVisualTokens.addSearchVisualToken(lastPartial);
+ const lastPartial = FilteredSearchVisualTokens.getLastTokenPartial();
+ FilteredSearchVisualTokens.removeLastTokenPartial();
+ FilteredSearchVisualTokens.addSearchVisualToken(lastPartial);
}
tokenContainer.removeChild(inputLi);
@@ -337,6 +338,3 @@ class FilteredSearchVisualTokens {
}
}
}
-
-window.gl = window.gl || {};
-gl.FilteredSearchVisualTokens = FilteredSearchVisualTokens;
diff --git a/app/assets/javascripts/filtered_search/recent_searches_root.js b/app/assets/javascripts/filtered_search/recent_searches_root.js
index c99ed63c4af..f9338b82acf 100644
--- a/app/assets/javascripts/filtered_search/recent_searches_root.js
+++ b/app/assets/javascripts/filtered_search/recent_searches_root.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import RecentSearchesDropdownContent from './components/recent_searches_dropdown_content';
+import RecentSearchesDropdownContent from './components/recent_searches_dropdown_content.vue';
import eventHub from './event_hub';
class RecentSearchesRoot {
@@ -33,7 +33,7 @@ class RecentSearchesRoot {
this.vm = new Vue({
el: this.wrapperElement,
components: {
- 'recent-searches-dropdown-content': RecentSearchesDropdownContent,
+ RecentSearchesDropdownContent,
},
data() { return state; },
template: `
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 15df7a7f989..6cf78bab6ad 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -485,7 +485,7 @@ GitLabDropdown = (function() {
$target = $(e.target);
if ($target && !$target.hasClass('dropdown-menu-close') &&
!$target.hasClass('dropdown-menu-close-icon') &&
- !$target.data('is-link')) {
+ !$target.data('isLink')) {
e.stopPropagation();
return false;
} else {
@@ -607,7 +607,20 @@ GitLabDropdown = (function() {
};
GitLabDropdown.prototype.renderItem = function(data, group, index) {
- var field, fieldName, html, selected, text, url, value;
+ var field, fieldName, html, selected, text, url, value, rowHidden;
+
+ if (!this.options.renderRow) {
+ value = this.options.id ? this.options.id(data) : data.id;
+
+ if (value) {
+ value = value.toString().replace(/'/g, '\\\'');
+ }
+ }
+
+ // Hide element
+ if (this.options.hideRow && this.options.hideRow(value)) {
+ rowHidden = true;
+ }
if (group == null) {
group = false;
}
@@ -616,6 +629,7 @@ GitLabDropdown = (function() {
index = false;
}
html = document.createElement('li');
+
if (data === 'divider' || data === 'separator') {
html.className = data;
return html;
@@ -631,11 +645,9 @@ GitLabDropdown = (function() {
html = this.options.renderRow.call(this.options, data, this);
} else {
if (!selected) {
- value = this.options.id ? this.options.id(data) : data.id;
fieldName = this.options.fieldName;
if (value) {
- value = value.toString().replace(/'/g, '\\\'');
field = this.dropdown.parent().find(`input[name='${fieldName}'][value='${value}']`);
if (field.length) {
selected = true;
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index d0f9e6af0f8..2d40856e038 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -1,5 +1,4 @@
-/* global autosize */
-
+import autosize from 'autosize';
import GfmAutoComplete from './gfm_auto_complete';
import dropzoneInput from './dropzone_input';
import textUtils from './lib/utils/text_markdown';
@@ -13,7 +12,7 @@ export default class GLForm {
this.destroy();
// Setup the form
this.setupForm();
- this.form.data('gl-form', this);
+ this.form.data('glForm', this);
}
destroy() {
@@ -22,7 +21,7 @@ export default class GLForm {
if (this.autoComplete) {
this.autoComplete.destroy();
}
- this.form.data('gl-form', null);
+ this.form.data('glForm', null);
}
setupForm() {
diff --git a/app/assets/javascripts/gpg_badges.js b/app/assets/javascripts/gpg_badges.js
index 7ac9dcd1112..6bf21f4f27d 100644
--- a/app/assets/javascripts/gpg_badges.js
+++ b/app/assets/javascripts/gpg_badges.js
@@ -1,3 +1,8 @@
+import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
+import axios from '~/lib/utils/axios_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
+
export default class GpgBadges {
static fetch() {
const badges = $('.js-loading-gpg-badge');
@@ -5,13 +10,13 @@ export default class GpgBadges {
badges.html('<i class="fa fa-spinner fa-spin"></i>');
- $.get({
- url: form.data('signatures-path'),
- data: form.serialize(),
- }).done((response) => {
- response.signatures.forEach((signature) => {
+ const params = parseQueryStringIntoObject(form.serialize());
+ return axios.get(form.data('signaturesPath'), { params })
+ .then(({ data }) => {
+ data.signatures.forEach((signature) => {
badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
});
- });
+ })
+ .catch(() => flash(__('An error occurred while loading commits')));
}
}
diff --git a/app/assets/javascripts/graphs/graphs_bundle.js b/app/assets/javascripts/graphs/graphs_bundle.js
deleted file mode 100644
index 534bc535bb6..00000000000
--- a/app/assets/javascripts/graphs/graphs_bundle.js
+++ /dev/null
@@ -1,4 +0,0 @@
-import Chart from 'vendor/Chart';
-
-// export to global scope
-window.Chart = Chart;
diff --git a/app/assets/javascripts/groups/components/item_stats_value.vue b/app/assets/javascripts/groups/components/item_stats_value.vue
index 08d0bf6e344..4d86ac8023c 100644
--- a/app/assets/javascripts/groups/components/item_stats_value.vue
+++ b/app/assets/javascripts/groups/components/item_stats_value.vue
@@ -30,11 +30,11 @@
default: 'bottom',
},
/**
- * value could either be number or string
- * as `memberCount` is always passed as string
- * while `subgroupCount` & `projectCount`
- * are always number
- */
+ * value could either be number or string
+ * as `memberCount` is always passed as string
+ * while `subgroupCount` & `projectCount`
+ * are always number
+ */
value: {
type: [Number, String],
required: false,
diff --git a/app/assets/javascripts/groups_select.js b/app/assets/javascripts/groups_select.js
index a69a0bde17b..12fc5f9b5c9 100644
--- a/app/assets/javascripts/groups_select.js
+++ b/app/assets/javascripts/groups_select.js
@@ -1,13 +1,14 @@
+import axios from './lib/utils/axios_utils';
import Api from './api';
-import { normalizeCRLFHeaders } from './lib/utils/common_utils';
+import { normalizeHeaders } from './lib/utils/common_utils';
export default function groupsSelect() {
// Needs to be accessible in rspec
window.GROUP_SELECT_PER_PAGE = 20;
$('.ajax-groups-select').each(function setAjaxGroupsSelect2() {
const $select = $(this);
- const allAvailable = $select.data('all-available');
- const skipGroups = $select.data('skip-groups') || [];
+ const allAvailable = $select.data('allAvailable');
+ const skipGroups = $select.data('skipGroups') || [];
$select.select2({
placeholder: 'Search for a group',
multiple: $select.hasClass('multiselect'),
@@ -17,24 +18,23 @@ export default function groupsSelect() {
dataType: 'json',
quietMillis: 250,
transport(params) {
- return $.ajax(params)
- .then((data, status, xhr) => {
- const results = data || [];
-
- const headers = normalizeCRLFHeaders(xhr.getAllResponseHeaders());
+ axios[params.type.toLowerCase()](params.url, {
+ params: params.data,
+ })
+ .then((res) => {
+ const results = res.data || [];
+ const headers = normalizeHeaders(res.headers);
const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
const more = currentPage < totalPages;
- return {
+ params.success({
results,
pagination: {
more,
},
- };
- })
- .then(params.success)
- .fail(params.error);
+ });
+ }).catch(params.error);
},
data(search, page) {
return {
diff --git a/app/assets/javascripts/help/help.js b/app/assets/javascripts/help/help.js
index 4a22ebf187d..d02477b19a2 100644
--- a/app/assets/javascripts/help/help.js
+++ b/app/assets/javascripts/help/help.js
@@ -1,6 +1,8 @@
// We will render the icons list here
-if ($('#user-content-gitlab-icons').length > 0) {
- const $iconsHeader = $('#user-content-gitlab-icons');
- const $iconsList = $('<div id="iconsList">ICONS</div>');
- $($iconsList).insertAfter($iconsHeader.parent());
-}
+export default () => {
+ if ($('#user-content-gitlab-icons').length > 0) {
+ const $iconsHeader = $('#user-content-gitlab-icons');
+ const $iconsList = $('<div id="iconsList">ICONS</div>');
+ $($iconsList).insertAfter($iconsHeader.parent());
+ }
+};
diff --git a/app/assets/javascripts/how_to_merge.js b/app/assets/javascripts/how_to_merge.js
index 19f4a946f73..12e6f24595a 100644
--- a/app/assets/javascripts/how_to_merge.js
+++ b/app/assets/javascripts/how_to_merge.js
@@ -1,12 +1,13 @@
-document.addEventListener('DOMContentLoaded', () => {
- const modal = $('#modal_merge_info').modal({
- modal: true,
- show: false,
- });
- $('.how_to_merge_link').on('click', () => {
- modal.show();
- });
- $('.modal-header .close').on('click', () => {
- modal.hide();
- });
-});
+export default () => {
+ const modal = $('#modal_merge_info');
+
+ if (modal) {
+ modal.modal({
+ modal: true,
+ show: false,
+ });
+
+ $('.how_to_merge_link').on('click', modal.show);
+ $('.modal-header .close').on('click', modal.hide);
+ }
+};
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index 110918872fb..cbbab765e1c 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -1,9 +1,9 @@
<script>
import { mapState } from 'vuex';
- import timeAgoMixin from '../../vue_shared/mixins/timeago';
- import skeletonLoadingContainer from '../../vue_shared/components/skeleton_loading_container.vue';
+ import timeAgoMixin from '~/vue_shared/mixins/timeago';
+ import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
+ import fileIcon from '~/vue_shared/components/file_icon.vue';
import newDropdown from './new_dropdown/index.vue';
- import fileIcon from '../../vue_shared/components/file_icon.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index 5ed7bddf6ae..5656081c598 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -1,6 +1,6 @@
<script>
import { mapActions } from 'vuex';
- import fileIcon from '../../vue_shared/components/file_icon.vue';
+ import fileIcon from '~/vue_shared/components/file_icon.vue';
export default {
components: {
diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js
index af83a1ec0b4..142a220097b 100644
--- a/app/assets/javascripts/ide/monaco_loader.js
+++ b/app/assets/javascripts/ide/monaco_loader.js
@@ -6,6 +6,11 @@ monacoContext.require.config({
},
});
+// ignore CDN config and use local assets path for service worker which cannot be cross-domain
+const relativeRootPath = (gon && gon.relative_url_root) || '';
+const monacoPath = `${relativeRootPath}/assets/webpack/monaco-editor/vs`;
+window.MonacoEnvironment = { getWorkerUrl: () => `${monacoPath}/base/worker/workerMain.js` };
+
// eslint-disable-next-line no-underscore-dangle
window.__monaco_context__ = monacoContext;
export default monacoContext.require;
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js
index 1dc70872d92..35094f8e73b 100644
--- a/app/assets/javascripts/importer_status.js
+++ b/app/assets/javascripts/importer_status.js
@@ -1,3 +1,7 @@
+import { __ } from './locale';
+import axios from './lib/utils/axios_utils';
+import flash from './flash';
+
class ImporterStatus {
constructor(jobsUrl, importUrl) {
this.jobsUrl = jobsUrl;
@@ -9,29 +13,7 @@ class ImporterStatus {
initStatusPage() {
$('.js-add-to-import')
.off('click')
- .on('click', (event) => {
- const $btn = $(event.currentTarget);
- const $tr = $btn.closest('tr');
- const $targetField = $tr.find('.import-target');
- const $namespaceInput = $targetField.find('.js-select-namespace option:selected');
- const id = $tr.attr('id').replace('repo_', '');
- let targetNamespace;
- let newName;
- if ($namespaceInput.length > 0) {
- targetNamespace = $namespaceInput[0].innerHTML;
- newName = $targetField.find('#path').prop('value');
- $targetField.empty().append(`${targetNamespace}/${newName}`);
- }
- $btn.disable().addClass('is-loading');
-
- return $.post(this.importUrl, {
- repo_id: id,
- target_namespace: targetNamespace,
- new_name: newName,
- }, {
- dataType: 'script',
- });
- });
+ .on('click', this.addToImport.bind(this));
$('.js-import-all')
.off('click')
@@ -44,34 +26,74 @@ class ImporterStatus {
});
}
- setAutoUpdate() {
- return setInterval(() => $.get(this.jobsUrl, data => $.each(data, (i, job) => {
- const jobItem = $(`#project_${job.id}`);
- const statusField = jobItem.find('.job-status');
+ addToImport(event) {
+ const $btn = $(event.currentTarget);
+ const $tr = $btn.closest('tr');
+ const $targetField = $tr.find('.import-target');
+ const $namespaceInput = $targetField.find('.js-select-namespace option:selected');
+ const id = $tr.attr('id').replace('repo_', '');
+ let targetNamespace;
+ let newName;
+ if ($namespaceInput.length > 0) {
+ targetNamespace = $namespaceInput[0].innerHTML;
+ newName = $targetField.find('#path').prop('value');
+ $targetField.empty().append(`${targetNamespace}/${newName}`);
+ }
+ $btn.disable().addClass('is-loading');
+
+ return axios.post(this.importUrl, {
+ repo_id: id,
+ target_namespace: targetNamespace,
+ new_name: newName,
+ })
+ .then(({ data }) => {
+ const job = $(`tr#repo_${id}`);
+ job.attr('id', `project_${data.id}`);
- const spinner = '<i class="fa fa-spinner fa-spin"></i>';
+ job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`);
+ $('table.import-jobs tbody').prepend(job);
- switch (job.import_status) {
- case 'finished':
- jobItem.removeClass('active').addClass('success');
- statusField.html('<span><i class="fa fa-check"></i> done</span>');
- break;
- case 'scheduled':
- statusField.html(`${spinner} scheduled`);
- break;
- case 'started':
- statusField.html(`${spinner} started`);
- break;
- default:
- statusField.html(job.import_status);
- break;
- }
- })), 4000);
+ job.addClass('active');
+ job.find('.import-actions').html('<i class="fa fa-spinner fa-spin" aria-label="importing"></i> started');
+ })
+ .catch(() => flash(__('An error occurred while importing project')));
+ }
+
+ autoUpdate() {
+ return axios.get(this.jobsUrl)
+ .then(({ data = [] }) => {
+ data.forEach((job) => {
+ const jobItem = $(`#project_${job.id}`);
+ const statusField = jobItem.find('.job-status');
+
+ const spinner = '<i class="fa fa-spinner fa-spin"></i>';
+
+ switch (job.import_status) {
+ case 'finished':
+ jobItem.removeClass('active').addClass('success');
+ statusField.html('<span><i class="fa fa-check"></i> done</span>');
+ break;
+ case 'scheduled':
+ statusField.html(`${spinner} scheduled`);
+ break;
+ case 'started':
+ statusField.html(`${spinner} started`);
+ break;
+ default:
+ statusField.html(job.import_status);
+ break;
+ }
+ });
+ });
+ }
+
+ setAutoUpdate() {
+ setInterval(this.autoUpdate.bind(this), 4000);
}
}
// eslint-disable-next-line consistent-return
-export default function initImporterStatus() {
+function initImporterStatus() {
const importerStatus = document.querySelector('.js-importer-status');
if (importerStatus) {
@@ -79,3 +101,8 @@ export default function initImporterStatus() {
return new ImporterStatus(data.jobsImportPath, data.importPath);
}
}
+
+export {
+ initImporterStatus as default,
+ ImporterStatus,
+};
diff --git a/app/assets/javascripts/integrations/index.js b/app/assets/javascripts/integrations/index.js
deleted file mode 100644
index 10fe6bac0e8..00000000000
--- a/app/assets/javascripts/integrations/index.js
+++ /dev/null
@@ -1,7 +0,0 @@
-/* eslint-disable no-new */
-import IntegrationSettingsForm from './integration_settings_form';
-
-$(() => {
- const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
- integrationSettingsForm.init();
-});
diff --git a/app/assets/javascripts/integrations/integration_settings_form.js b/app/assets/javascripts/integrations/integration_settings_form.js
index 3f27cfc2f6d..2848fe003cb 100644
--- a/app/assets/javascripts/integrations/integration_settings_form.js
+++ b/app/assets/javascripts/integrations/integration_settings_form.js
@@ -6,8 +6,8 @@ export default class IntegrationSettingsForm {
this.$form = $(formSelector);
// Form Metadata
- this.canTestService = this.$form.data('can-test');
- this.testEndPoint = this.$form.data('test-url');
+ this.canTestService = this.$form.data('canTest');
+ this.testEndPoint = this.$form.data('testUrl');
// Form Child Elements
this.$serviceToggle = this.$form.find('#service_active');
diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js
index ff65ea99e9a..333bbd9e0ba 100644
--- a/app/assets/javascripts/issue.js
+++ b/app/assets/javascripts/issue.js
@@ -1,5 +1,4 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */
-import 'vendor/jquery.waitforimages';
import axios from './lib/utils/axios_utils';
import { addDelimiter } from './lib/utils/text_utility';
import flash from './flash';
@@ -25,6 +24,51 @@ export default class Issue {
if (Issue.createMrDropdownWrap) {
this.createMergeRequestDropdown = new CreateMergeRequestDropdown(Issue.createMrDropdownWrap);
}
+
+ // Listen to state changes in the Vue app
+ document.addEventListener('issuable_vue_app:change', (event) => {
+ this.updateTopState(event.detail.isClosed, event.detail.data);
+ });
+ }
+
+ /**
+ * This method updates the top area of the issue.
+ *
+ * Once the issue state changes, either through a click on the top area (jquery)
+ * or a click on the bottom area (Vue) we need to update the top area.
+ *
+ * @param {Boolean} isClosed
+ * @param {Array} data
+ * @param {String} issueFailMessage
+ */
+ updateTopState(isClosed, data, issueFailMessage = 'Unable to update this issue at this time.') {
+ if ('id' in data) {
+ const isClosedBadge = $('div.status-box-issue-closed');
+ const isOpenBadge = $('div.status-box-open');
+ const projectIssuesCounter = $('.issue_counter');
+
+ isClosedBadge.toggleClass('hidden', !isClosed);
+ isOpenBadge.toggleClass('hidden', isClosed);
+
+ $(document).trigger('issuable:change', isClosed);
+ this.toggleCloseReopenButton(isClosed);
+
+ let numProjectIssues = Number(projectIssuesCounter.first().text().trim().replace(/[^\d]/, ''));
+ numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
+ projectIssuesCounter.text(addDelimiter(numProjectIssues));
+
+ if (this.createMergeRequestDropdown) {
+ if (isClosed) {
+ this.createMergeRequestDropdown.unavailable();
+ this.createMergeRequestDropdown.disable();
+ } else {
+ // We should check in case a branch was created in another tab
+ this.createMergeRequestDropdown.checkAbilityToCreateBranch();
+ }
+ }
+ } else {
+ flash(issueFailMessage);
+ }
}
initIssueBtnEventListeners() {
@@ -45,34 +89,8 @@ export default class Issue {
url = $button.attr('href');
return axios.put(url)
.then(({ data }) => {
- const isClosedBadge = $('div.status-box-issue-closed');
- const isOpenBadge = $('div.status-box-open');
- const projectIssuesCounter = $('.issue_counter');
-
- if ('id' in data) {
- const isClosed = $button.hasClass('btn-close');
- isClosedBadge.toggleClass('hidden', !isClosed);
- isOpenBadge.toggleClass('hidden', isClosed);
-
- $(document).trigger('issuable:change', isClosed);
- this.toggleCloseReopenButton(isClosed);
-
- let numProjectIssues = Number(projectIssuesCounter.first().text().trim().replace(/[^\d]/, ''));
- numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1;
- projectIssuesCounter.text(addDelimiter(numProjectIssues));
-
- if (this.createMergeRequestDropdown) {
- if (isClosed) {
- this.createMergeRequestDropdown.unavailable();
- this.createMergeRequestDropdown.disable();
- } else {
- // We should check in case a branch was created in another tab
- this.createMergeRequestDropdown.checkAbilityToCreateBranch();
- }
- }
- } else {
- flash(issueFailMessage);
- }
+ const isClosed = $button.hasClass('btn-close');
+ this.updateTopState(isClosed, data);
})
.catch(() => flash(issueFailMessage))
.then(() => {
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index 9afa9dea126..1338be0ec4b 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -78,6 +78,7 @@
taskListUpdateSuccess(data) {
try {
this.checkForSpam(data);
+ this.closeRecaptcha();
} catch (error) {
if (error && error.name === 'SpamError') this.openRecaptcha();
}
diff --git a/app/assets/javascripts/issue_status_select.js b/app/assets/javascripts/issue_status_select.js
index 03546f61d1f..71c0f894389 100644
--- a/app/assets/javascripts/issue_status_select.js
+++ b/app/assets/javascripts/issue_status_select.js
@@ -1,6 +1,6 @@
export default function issueStatusSelect() {
$('.js-issue-status').each((i, el) => {
- const fieldName = $(el).data('field-name');
+ const fieldName = $(el).data('fieldName');
return $(el).glDropdown({
selectable: true,
fieldName,
diff --git a/app/assets/javascripts/jobs/job_details_bundle.js b/app/assets/javascripts/jobs/job_details_bundle.js
index db53b04de0e..85a88ae409b 100644
--- a/app/assets/javascripts/jobs/job_details_bundle.js
+++ b/app/assets/javascripts/jobs/job_details_bundle.js
@@ -3,7 +3,7 @@ import JobMediator from './job_details_mediator';
import jobHeader from './components/header.vue';
import detailsBlock from './components/sidebar_details_block.vue';
-document.addEventListener('DOMContentLoaded', () => {
+export default () => {
const dataset = document.getElementById('js-job-details-vue').dataset;
const mediator = new JobMediator({ endpoint: dataset.endpoint });
@@ -55,4 +55,4 @@ document.addEventListener('DOMContentLoaded', () => {
});
},
});
-});
+};
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index 5ecf81ad11d..7151ac05a09 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -25,19 +25,19 @@ export default class LabelsSelect {
$dropdown = $(dropdown);
$dropdownContainer = $dropdown.closest('.labels-filter');
$toggleText = $dropdown.find('.dropdown-toggle-text');
- namespacePath = $dropdown.data('namespace-path');
- projectPath = $dropdown.data('project-path');
+ namespacePath = $dropdown.data('namespacePath');
+ projectPath = $dropdown.data('projectPath');
labelUrl = $dropdown.data('labels');
issueUpdateURL = $dropdown.data('issueUpdate');
selectedLabel = $dropdown.data('selected');
if ((selectedLabel != null) && !$dropdown.hasClass('js-multiselect')) {
selectedLabel = selectedLabel.split(',');
}
- showNo = $dropdown.data('show-no');
- showAny = $dropdown.data('show-any');
+ showNo = $dropdown.data('showNo');
+ showAny = $dropdown.data('showAny');
showMenuAbove = $dropdown.data('showMenuAbove');
- defaultLabel = $dropdown.data('default-label');
- abilityName = $dropdown.data('ability-name');
+ defaultLabel = $dropdown.data('defaultLabel');
+ abilityName = $dropdown.data('abilityName');
$selectbox = $dropdown.closest('.selectbox');
$block = $selectbox.closest('.block');
$form = $dropdown.closest('form, .js-issuable-update');
@@ -45,11 +45,11 @@ export default class LabelsSelect {
$sidebarLabelTooltip = $block.find('.js-sidebar-labels-tooltip');
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
- fieldName = $dropdown.data('field-name');
+ fieldName = $dropdown.data('fieldName');
useId = $dropdown.is('.js-issuable-form-dropdown, .js-filter-bulk-update, .js-label-sidebar-dropdown');
propertyName = useId ? 'id' : 'title';
initialSelected = $selectbox
- .find('input[name="' + $dropdown.data('field-name') + '"]')
+ .find('input[name="' + $dropdown.data('fieldName') + '"]')
.map(function () {
return this.value;
}).get();
@@ -213,7 +213,7 @@ export default class LabelsSelect {
}
}
if (label.duplicate) {
- color = gl.DropdownUtils.duplicateLabelColor(label.color);
+ color = DropdownUtils.duplicateLabelColor(label.color);
}
else {
if (label.color != null) {
@@ -268,7 +268,7 @@ export default class LabelsSelect {
return defaultLabel;
}
},
- fieldName: $dropdown.data('field-name'),
+ fieldName: $dropdown.data('fieldName'),
id: function(label) {
if (label.id <= 0) return label.title;
@@ -316,9 +316,9 @@ export default class LabelsSelect {
},
multiSelect: $dropdown.hasClass('js-multiselect'),
vue: $dropdown.hasClass('js-issue-board-sidebar'),
- clicked: function(options) {
- const { $el, e, isMarking } = options;
- const label = options.selectedObj;
+ clicked: function (clickEvent) {
+ const { $el, e, isMarking } = clickEvent;
+ const label = clickEvent.selectedObj;
var isIssueIndex, isMRIndex, page, boardsModel;
var fadeOutLoader = () => {
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index ab3cc29146a..1b4900827b8 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -4,7 +4,7 @@ import initFlyOutNav from './fly_out_nav';
function hideEndFade($scrollingTabs) {
$scrollingTabs.each(function scrollTabsLoop() {
const $this = $(this);
- $this.siblings('.fade-right').toggleClass('scrolling', $this.width() < $this.prop('scrollWidth'));
+ $this.siblings('.fade-right').toggleClass('scrolling', Math.round($this.width()) < $this.prop('scrollWidth'));
});
}
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 7d2cf4b634f..017f3b986fd 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -418,6 +418,16 @@ export const convertObjectPropsToCamelCase = (obj = {}) => {
export const imagePath = imgUrl => `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
+export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
+ // Click a .js-select-on-focus field, select the contents
+ // Prevent a mouseup event from deselecting the input
+ $(selector).on('focusin', function selectOnFocusCallback() {
+ $(this).select().one('mouseup', (e) => {
+ e.preventDefault();
+ });
+ });
+};
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index 2dc9cf0cc29..5dc98b4a920 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -138,7 +138,7 @@ textUtils.init = function(form) {
return $('.js-md', form).off('click').on('click', function() {
var $this;
$this = $(this);
- return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend'));
+ return self.updateText($this.closest('.md-area').find('textarea'), $this.data('mdTag'), $this.data('mdBlock'), !$this.data('mdPrepend'));
});
};
diff --git a/app/assets/javascripts/line_highlighter.js b/app/assets/javascripts/line_highlighter.js
index fbd381d8ff7..e5c1fce3db9 100644
--- a/app/assets/javascripts/line_highlighter.js
+++ b/app/assets/javascripts/line_highlighter.js
@@ -83,7 +83,7 @@ LineHighlighter.prototype.clickHandler = function(event) {
var current, lineNumber, range;
event.preventDefault();
this.clearHighlight();
- lineNumber = $(event.target).closest('a').data('line-number');
+ lineNumber = $(event.target).closest('a').data('lineNumber');
current = this.hashToRange(this._hash);
if (!(current[0] && event.shiftKey)) {
// If there's no current selection, or there is but Shift wasn't held,
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index b99cb257ce3..659dc9eaa1f 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -10,7 +10,7 @@ window.jQuery = jQuery;
window.$ = jQuery;
// lib/utils
-import { handleLocationHash } from './lib/utils/common_utils';
+import { handleLocationHash, addSelectOnFocusBehaviour } from './lib/utils/common_utils';
import { localTimeAgo } from './lib/utils/datetime_utility';
import { getLocationHash, visitUrl } from './lib/utils/url_utility';
@@ -61,7 +61,7 @@ gl.lazyLoader = new LazyLoader({
observerNode: '#content-body',
});
-$(() => {
+document.addEventListener('DOMContentLoaded', () => {
const $body = $('body');
const $document = $(document);
const $window = $(window);
@@ -104,13 +104,7 @@ $(() => {
return true;
});
- // Click a .js-select-on-focus field, select the contents
- // Prevent a mouseup event from deselecting the input
- $('.js-select-on-focus').on('focusin', function selectOnFocusCallback() {
- $(this).select().one('mouseup', (e) => {
- e.preventDefault();
- });
- });
+ addSelectOnFocusBehaviour('.js-select-on-focus');
$('.remove-row').on('ajax:success', function removeRowAjaxSuccessCallback() {
$(this).tooltip('destroy')
@@ -220,7 +214,7 @@ $(() => {
$document.on('click', '.js-confirm-danger', (e) => {
const btn = $(e.target);
const form = btn.closest('form');
- const text = btn.data('confirm-danger-message');
+ const text = btn.data('confirmDangerMessage');
e.preventDefault();
// eslint-disable-next-line no-new
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index 52315e969d1..330ebed5f73 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -19,7 +19,7 @@ export default class Members {
isSelectable(selected, $el) {
return !$el.hasClass('is-active');
},
- fieldName: $btn.data('field-name'),
+ fieldName: $btn.data('fieldName'),
id(selected, $el) {
return $el.data('id');
},
@@ -51,7 +51,7 @@ export default class Members {
}
// eslint-disable-next-line class-methods-use-this
getMemberListItems($el) {
- const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('el-id')}`);
+ const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('elId')}`);
return {
$memberListItem,
diff --git a/app/assets/javascripts/merge_request.js b/app/assets/javascripts/merge_request.js
index bedd50de1bb..a64093afcf4 100644
--- a/app/assets/javascripts/merge_request.js
+++ b/app/assets/javascripts/merge_request.js
@@ -1,6 +1,4 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */
-
-import 'vendor/jquery.waitforimages';
import { __ } from '~/locale';
import TaskList from './task_list';
import MergeRequestTabs from './merge_request_tabs';
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 3e97a8c758d..41971e92ec0 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -361,7 +361,7 @@ export default class MergeRequestTabs {
}
diffViewType() {
- return $('.inline-parallel-buttons a.active').data('view-type');
+ return $('.inline-parallel-buttons a.active').data('viewType');
}
isDiffAction(action) {
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 6581be606eb..2841ecb558b 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -24,19 +24,19 @@ export default class MilestoneSelect {
$els.each((i, dropdown) => {
let collapsedSidebarLabelTemplate, milestoneLinkNoneTemplate, milestoneLinkTemplate, selectedMilestone, selectedMilestoneDefault;
const $dropdown = $(dropdown);
- const projectId = $dropdown.data('project-id');
+ const projectId = $dropdown.data('projectId');
const milestonesUrl = $dropdown.data('milestones');
const issueUpdateURL = $dropdown.data('issueUpdate');
- const showNo = $dropdown.data('show-no');
- const showAny = $dropdown.data('show-any');
+ const showNo = $dropdown.data('showNo');
+ const showAny = $dropdown.data('showAny');
const showMenuAbove = $dropdown.data('showMenuAbove');
- const showUpcoming = $dropdown.data('show-upcoming');
- const showStarted = $dropdown.data('show-started');
- const useId = $dropdown.data('use-id');
- const defaultLabel = $dropdown.data('default-label');
- const defaultNo = $dropdown.data('default-no');
- const issuableId = $dropdown.data('issuable-id');
- const abilityName = $dropdown.data('ability-name');
+ const showUpcoming = $dropdown.data('showUpcoming');
+ const showStarted = $dropdown.data('showStarted');
+ const useId = $dropdown.data('useId');
+ const defaultLabel = $dropdown.data('defaultLabel');
+ const defaultNo = $dropdown.data('defaultNo');
+ const issuableId = $dropdown.data('issuableId');
+ const abilityName = $dropdown.data('abilityName');
const $selectBox = $dropdown.closest('.selectbox');
const $block = $selectBox.closest('.block');
const $sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
@@ -114,7 +114,7 @@ export default class MilestoneSelect {
}
},
defaultLabel: defaultLabel,
- fieldName: $dropdown.data('field-name'),
+ fieldName: $dropdown.data('fieldName'),
text: milestone => _.escape(milestone.title),
id: (milestone) => {
if (!useId && !$dropdown.is('.js-issuable-form-dropdown')) {
@@ -166,7 +166,7 @@ export default class MilestoneSelect {
}
if (boardsStore) {
- boardsStore[$dropdown.data('field-name')] = selected.name;
+ boardsStore[$dropdown.data('fieldName')] = selected.name;
e.preventDefault();
} else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
return Issuable.filterResults($dropdown.closest('form'));
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 8efb8ac5320..f17b432cffd 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -219,7 +219,7 @@ export default class Notes {
}
editNote = $textarea.closest('.note');
if (editNote.length) {
- originalText = $textarea.closest('form').data('original-note');
+ originalText = $textarea.closest('form').data('originalNote');
newText = $textarea.val();
if (originalText !== newText) {
if (!confirm('Are you sure you want to cancel editing this comment?')) {
@@ -609,9 +609,9 @@ export default class Notes {
*/
addDiscussionNote($form, note, isNewDiffComment) {
if ($form.attr('data-resolve-all') != null) {
- var projectPath = $form.data('project-path');
- var discussionId = $form.data('discussion-id');
- var mergeRequestId = $form.data('noteable-iid');
+ var projectPath = $form.data('projectPath');
+ var discussionId = $form.data('discussionId');
+ var mergeRequestId = $form.data('noteableIid');
if (ResolveService != null) {
ResolveService.toggleResolveForDiscussion(mergeRequestId, discussionId);
@@ -751,7 +751,7 @@ export default class Notes {
form.removeClass('current-note-edit-form');
form.find('.js-finish-edit-warning').hide();
// Replace markdown textarea text with original note text.
- return form.find('.js-note-text').val(form.find('form.edit-note').data('original-note'));
+ return form.find('.js-note-text').val(form.find('form.edit-note').data('originalNote'));
}
/**
@@ -776,7 +776,7 @@ export default class Notes {
var $note, $notes;
$note = $(el);
$notes = $note.closest('.discussion-notes');
- const discussionId = $('.notes', $notes).data('discussion-id');
+ const discussionId = $('.notes', $notes).data('discussionId');
if (typeof gl.diffNotesCompileComponents !== 'undefined') {
if (gl.diffNoteApps[noteElId]) {
@@ -897,7 +897,7 @@ export default class Notes {
// DiffNote
form.find('#note_position').val(dataHolder.attr('data-position'));
- form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancel-text'));
+ form.find('.js-note-discard').show().removeClass('js-note-discard').addClass('js-close-discussion-note-form').text(form.find('.js-close-discussion-note-form').data('cancelText'));
form.find('.js-note-target-close').remove();
form.find('.js-note-new-discussion').remove();
this.setupNoteForm(form);
@@ -1037,7 +1037,7 @@ export default class Notes {
removeDiscussionNoteForm(form) {
var glForm, row;
row = form.closest('tr');
- glForm = form.data('gl-form');
+ glForm = form.data('glForm');
glForm.destroy();
form.find('.js-note-text').data('autosave').reset();
// show the reply button (will only work for replies)
@@ -1122,8 +1122,8 @@ export default class Notes {
return discardbtn.show();
}
} else {
- reopentext = reopenbtn.data('original-text');
- closetext = closebtn.data('original-text');
+ reopentext = reopenbtn.data('originalText');
+ closetext = closebtn.data('originalText');
if (reopenbtn.text() !== reopentext) {
reopenbtn.text(reopentext);
}
@@ -1150,9 +1150,9 @@ export default class Notes {
var $originalContentEl = $note.find('.original-note-content');
var originalContent = $originalContentEl.text().trim();
- var postUrl = $originalContentEl.data('post-url');
- var targetId = $originalContentEl.data('target-id');
- var targetType = $originalContentEl.data('target-type');
+ var postUrl = $originalContentEl.data('postUrl');
+ var targetId = $originalContentEl.data('targetId');
+ var targetType = $originalContentEl.data('targetType');
this.glForm = new GLForm($editForm.find('form'), this.enableGFM);
@@ -1513,9 +1513,9 @@ export default class Notes {
// If comment intends to resolve discussion, do the same.
if (isDiscussionResolve) {
$form
- .attr('data-discussion-id', $submitBtn.data('discussion-id'))
+ .attr('data-discussion-id', $submitBtn.data('discussionId'))
.attr('data-resolve-all', 'true')
- .attr('data-project-path', $submitBtn.data('project-path'));
+ .attr('data-project-path', $submitBtn.data('projectPath'));
}
// Show final note element on UI
@@ -1587,7 +1587,7 @@ export default class Notes {
this.addNoteError($form);
});
- return $closeBtn.text($closeBtn.data('original-text'));
+ return $closeBtn.text($closeBtn.data('originalText'));
}
/**
@@ -1642,7 +1642,7 @@ export default class Notes {
this.updateNoteError();
});
- return $closeBtn.text($closeBtn.data('original-text'));
+ return $closeBtn.text($closeBtn.data('originalText'));
}
}
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 3c8452ac808..df796050e0d 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -2,16 +2,18 @@
import { mapActions, mapGetters } from 'vuex';
import _ from 'underscore';
import Autosize from 'autosize';
+ import { __ } from '~/locale';
import Flash from '../../flash';
import Autosave from '../../autosave';
import TaskList from '../../task_list';
import * as constants from '../constants';
import eventHub from '../event_hub';
import issueWarning from '../../vue_shared/components/issue/issue_warning.vue';
- import noteSignedOutWidget from './note_signed_out_widget.vue';
- import discussionLockedWidget from './discussion_locked_widget.vue';
import markdownField from '../../vue_shared/components/markdown/field.vue';
import userAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
+ import loadingButton from '../../vue_shared/components/loading_button.vue';
+ import noteSignedOutWidget from './note_signed_out_widget.vue';
+ import discussionLockedWidget from './discussion_locked_widget.vue';
import issuableStateMixin from '../mixins/issuable_state';
export default {
@@ -22,6 +24,7 @@
discussionLockedWidget,
markdownField,
userAvatarLink,
+ loadingButton,
},
mixins: [
issuableStateMixin,
@@ -30,9 +33,6 @@
return {
note: '',
noteType: constants.COMMENT,
- // Can't use mapGetters,
- // this needs to be in the data object because it belongs to the state
- issueState: this.$store.getters.getNoteableData.state,
isSubmitting: false,
isSubmitButtonDisabled: true,
};
@@ -43,6 +43,7 @@
'getUserData',
'getNoteableData',
'getNotesData',
+ 'issueState',
]),
isLoggedIn() {
return this.getUserData.id;
@@ -105,7 +106,7 @@
mounted() {
// jQuery is needed here because it is a custom event being dispatched with jQuery.
$(document).on('issuable:change', (e, isClosed) => {
- this.issueState = isClosed ? constants.CLOSED : constants.REOPENED;
+ this.toggleIssueLocalState(isClosed ? constants.CLOSED : constants.REOPENED);
});
this.initAutoSave();
@@ -117,6 +118,9 @@
'stopPolling',
'restartPolling',
'removePlaceholderNotes',
+ 'closeIssue',
+ 'reopenIssue',
+ 'toggleIssueLocalState',
]),
setIsSubmitButtonDisabled(note, isSubmitting) {
if (!_.isEmpty(note) && !isSubmitting) {
@@ -126,6 +130,8 @@
}
},
handleSave(withIssueAction) {
+ this.isSubmitting = true;
+
if (this.note.length) {
const noteData = {
endpoint: this.endpoint,
@@ -142,7 +148,6 @@
if (this.noteType === constants.DISCUSSION) {
noteData.data.note.type = constants.DISCUSSION_NOTE;
}
- this.isSubmitting = true;
this.note = ''; // Empty textarea while being requested. Repopulate in catch
this.resizeTextarea();
this.stopPolling();
@@ -184,13 +189,25 @@ Please check your network connection and try again.`;
this.toggleIssueState();
}
},
+ enableButton() {
+ this.isSubmitting = false;
+ },
toggleIssueState() {
- this.issueState = this.isIssueOpen ? constants.CLOSED : constants.REOPENED;
-
- // This is out of scope for the Notes Vue component.
- // It was the shortest path to update the issue state and relevant places.
- const btnClass = this.isIssueOpen ? 'btn-reopen' : 'btn-close';
- $(`.js-btn-issue-action.${btnClass}:visible`).trigger('click');
+ if (this.isIssueOpen) {
+ this.closeIssue()
+ .then(() => this.enableButton())
+ .catch(() => {
+ this.enableButton();
+ Flash(__('Something went wrong while closing the issue. Please try again later'));
+ });
+ } else {
+ this.reopenIssue()
+ .then(() => this.enableButton())
+ .catch(() => {
+ this.enableButton();
+ Flash(__('Something went wrong while reopening the issue. Please try again later'));
+ });
+ }
},
discard(shouldClear = true) {
// `blur` is needed to clear slash commands autocomplete cache if event fired.
@@ -367,15 +384,19 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
</li>
</ul>
</div>
- <button
- type="button"
- @click="handleSave(true)"
+
+ <loading-button
v-if="canUpdateIssue"
- :class="actionButtonClassNames"
+ :loading="isSubmitting"
+ @click="handleSave(true)"
+ :container-class="[
+ actionButtonClassNames,
+ 'btn btn-comment btn-comment-and-close js-action-button'
+ ]"
:disabled="isSubmitting"
- class="btn btn-comment btn-comment-and-close js-action-button">
- {{ issueActionButtonTitle }}
- </button>
+ :label="issueActionButtonTitle"
+ />
+
<button
type="button"
v-if="note.length"
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index d250dd8d25b..48e7cfddb63 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -28,6 +28,8 @@ document.addEventListener('DOMContentLoaded', () => new Vue({
notesPath: notesDataset.notesPath,
markdownDocsPath: notesDataset.markdownDocsPath,
quickActionsDocsPath: notesDataset.quickActionsDocsPath,
+ closeIssuePath: notesDataset.closeIssuePath,
+ reopenIssuePath: notesDataset.reopenIssuePath,
},
};
},
diff --git a/app/assets/javascripts/notes/services/notes_service.js b/app/assets/javascripts/notes/services/notes_service.js
index b51b0cb2013..b8e7ffc8c46 100644
--- a/app/assets/javascripts/notes/services/notes_service.js
+++ b/app/assets/javascripts/notes/services/notes_service.js
@@ -32,4 +32,7 @@ export default {
toggleAward(endpoint, data) {
return Vue.http.post(endpoint, data, { emulateJSON: true });
},
+ toggleIssueState(endpoint, data) {
+ return Vue.http.put(endpoint, data);
+ },
};
diff --git a/app/assets/javascripts/notes/stores/actions.js b/app/assets/javascripts/notes/stores/actions.js
index 085b18642ba..4c846d69b86 100644
--- a/app/assets/javascripts/notes/stores/actions.js
+++ b/app/assets/javascripts/notes/stores/actions.js
@@ -61,6 +61,39 @@ export const createNewNote = ({ commit }, { endpoint, data }) => service
export const removePlaceholderNotes = ({ commit }) =>
commit(types.REMOVE_PLACEHOLDER_NOTES);
+export const closeIssue = ({ commit, dispatch, state }) => service
+ .toggleIssueState(state.notesData.closeIssuePath)
+ .then(res => res.json())
+ .then((data) => {
+ commit(types.CLOSE_ISSUE);
+ dispatch('emitStateChangedEvent', data);
+ });
+
+export const reopenIssue = ({ commit, dispatch, state }) => service
+ .toggleIssueState(state.notesData.reopenIssuePath)
+ .then(res => res.json())
+ .then((data) => {
+ commit(types.REOPEN_ISSUE);
+ dispatch('emitStateChangedEvent', data);
+ });
+
+export const emitStateChangedEvent = ({ commit, getters }, data) => {
+ const event = new CustomEvent('issuable_vue_app:change', { detail: {
+ data,
+ isClosed: getters.issueState === constants.CLOSED,
+ } });
+
+ document.dispatchEvent(event);
+};
+
+export const toggleIssueLocalState = ({ commit }, newState) => {
+ if (newState === constants.CLOSED) {
+ commit(types.CLOSE_ISSUE);
+ } else if (newState === constants.REOPENED) {
+ commit(types.REOPEN_ISSUE);
+ }
+};
+
export const saveNote = ({ commit, dispatch }, noteData) => {
const { note } = noteData.data.note;
let placeholderText = note;
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index e18b277119e..82024104d73 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -8,6 +8,7 @@ export const getNotesDataByProp = state => prop => state.notesData[prop];
export const getNoteableData = state => state.noteableData;
export const getNoteableDataByProp = state => prop => state.noteableData[prop];
+export const issueState = state => state.noteableData.state;
export const getUserData = state => state.userData || {};
export const getUserDataByProp = state => prop => state.userData && state.userData[prop];
diff --git a/app/assets/javascripts/notes/stores/mutation_types.js b/app/assets/javascripts/notes/stores/mutation_types.js
index d520c197407..6d7c3bbae0f 100644
--- a/app/assets/javascripts/notes/stores/mutation_types.js
+++ b/app/assets/javascripts/notes/stores/mutation_types.js
@@ -12,3 +12,7 @@ export const SHOW_PLACEHOLDER_NOTE = 'SHOW_PLACEHOLDER_NOTE';
export const TOGGLE_AWARD = 'TOGGLE_AWARD';
export const TOGGLE_DISCUSSION = 'TOGGLE_DISCUSSION';
export const UPDATE_NOTE = 'UPDATE_NOTE';
+
+// Issue
+export const CLOSE_ISSUE = 'CLOSE_ISSUE';
+export const REOPEN_ISSUE = 'REOPEN_ISSUE';
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index 20f81a430c2..b3f66578c9a 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -152,4 +152,12 @@ export default {
noteObj.notes.splice(noteObj.notes.indexOf(comment), 1, note);
}
},
+
+ [types.CLOSE_ISSUE](state) {
+ Object.assign(state.noteableData, { state: constants.CLOSED });
+ },
+
+ [types.REOPEN_ISSUE](state) {
+ Object.assign(state.noteableData, { state: constants.REOPENED });
+ },
};
diff --git a/app/assets/javascripts/notifications_dropdown.js b/app/assets/javascripts/notifications_dropdown.js
index 9570d1c00aa..479a512ed65 100644
--- a/app/assets/javascripts/notifications_dropdown.js
+++ b/app/assets/javascripts/notifications_dropdown.js
@@ -3,11 +3,11 @@ import Flash from './flash';
export default function notificationsDropdown() {
$(document).on('click', '.update-notification', function updateNotificationCallback(e) {
e.preventDefault();
- if ($(this).is('.is-active') && $(this).data('notification-level') === 'custom') {
+ if ($(this).is('.is-active') && $(this).data('notificationLevel') === 'custom') {
return;
}
- const notificationLevel = $(this).data('notification-level');
+ const notificationLevel = $(this).data('notificationLevel');
const form = $(this).parents('.notification-form:first');
form.find('.js-notification-loading').toggleClass('fa-bell fa-spin fa-spinner');
diff --git a/app/assets/javascripts/pager.js b/app/assets/javascripts/pager.js
index fd3105b1960..7e85bce0d73 100644
--- a/app/assets/javascripts/pager.js
+++ b/app/assets/javascripts/pager.js
@@ -56,7 +56,7 @@ export default {
},
initLoadMore() {
- $(document).unbind('scroll');
+ $(document).off('scroll');
$(document).endlessScroll({
bottomPixels: ENDLESS_SCROLL_BOTTOM_PX,
fireDelay: ENDLESS_SCROLL_FIRE_DELAY_MS,
diff --git a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
index d87e6304a24..66702ec4ca0 100644
--- a/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
+++ b/app/assets/javascripts/pages/admin/abuse_reports/abuse_reports.js
@@ -15,21 +15,21 @@ export default class AbuseReports {
const $messageCellElement = $(this);
const reportMessage = $messageCellElement.text();
if (reportMessage.length > MAX_MESSAGE_LENGTH) {
- $messageCellElement.data('original-message', reportMessage);
- $messageCellElement.data('message-truncated', 'true');
+ $messageCellElement.data('originalMessage', reportMessage);
+ $messageCellElement.data('messageTruncated', 'true');
$messageCellElement.text(truncate(reportMessage, MAX_MESSAGE_LENGTH));
}
}
toggleMessageTruncation() {
const $messageCellElement = $(this);
- const originalMessage = $messageCellElement.data('original-message');
+ const originalMessage = $messageCellElement.data('originalMessage');
if (!originalMessage) return;
- if ($messageCellElement.data('message-truncated') === 'true') {
- $messageCellElement.data('message-truncated', 'false');
+ if ($messageCellElement.data('messageTruncated') === 'true') {
+ $messageCellElement.data('messageTruncated', 'false');
$messageCellElement.text(originalMessage);
} else {
- $messageCellElement.data('message-truncated', 'true');
+ $messageCellElement.data('messageTruncated', 'true');
$messageCellElement.text(`${originalMessage.substr(0, (MAX_MESSAGE_LENGTH - 3))}...`);
}
}
diff --git a/app/assets/javascripts/pages/admin/abuse_reports/index.js b/app/assets/javascripts/pages/admin/abuse_reports/index.js
index c0b6e8d4095..d76b1f174fc 100644
--- a/app/assets/javascripts/pages/admin/abuse_reports/index.js
+++ b/app/assets/javascripts/pages/admin/abuse_reports/index.js
@@ -1,3 +1,3 @@
import AbuseReports from './abuse_reports';
-export default () => new AbuseReports();
+document.addEventListener('DOMContentLoaded', () => new AbuseReports());
diff --git a/app/assets/javascripts/pages/admin/admin.js b/app/assets/javascripts/pages/admin/admin.js
index 135c15c346b..45e05f111a7 100644
--- a/app/assets/javascripts/pages/admin/admin.js
+++ b/app/assets/javascripts/pages/admin/admin.js
@@ -16,9 +16,9 @@ export default function adminInit() {
$('input#user_force_random_password').on('change', function randomPasswordClick() {
const $elems = $('#user_password, #user_password_confirmation');
if ($(this).attr('checked')) {
- $elems.val('').attr('disabled', true);
+ $elems.val('').prop('disabled', true);
} else {
- $elems.removeAttr('disabled');
+ $elems.prop('disabled', false);
}
});
diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js
index 885acfac6d0..f92450cbaa7 100644
--- a/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js
+++ b/app/assets/javascripts/pages/admin/broadcast_messages/broadcast_message.js
@@ -3,7 +3,7 @@ import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
-export default function initBroadcastMessagesForm() {
+export default () => {
$('input#broadcast_message_color').on('input', function onMessageColorInput() {
const previewColor = $(this).val();
$('div.broadcast-message-preview').css('background-color', previewColor);
@@ -14,7 +14,7 @@ export default function initBroadcastMessagesForm() {
$('div.broadcast-message-preview').css('color', previewColor);
});
- const previewPath = $('textarea#broadcast_message_message').data('preview-path');
+ const previewPath = $('textarea#broadcast_message_message').data('previewPath');
$('textarea#broadcast_message_message').on('input', _.debounce(function onMessageInput() {
const message = $(this).val();
@@ -32,4 +32,4 @@ export default function initBroadcastMessagesForm() {
.catch(() => flash(__('An error occurred while rendering preview broadcast message')));
}
}, 250));
-}
+};
diff --git a/app/assets/javascripts/pages/admin/broadcast_messages/index.js b/app/assets/javascripts/pages/admin/broadcast_messages/index.js
index b548c48282a..d6cc6a850eb 100644
--- a/app/assets/javascripts/pages/admin/broadcast_messages/index.js
+++ b/app/assets/javascripts/pages/admin/broadcast_messages/index.js
@@ -1,3 +1,3 @@
import initBroadcastMessagesForm from './broadcast_message';
-export default () => initBroadcastMessagesForm();
+document.addEventListener('DOMContentLoaded', initBroadcastMessagesForm);
diff --git a/app/assets/javascripts/pages/admin/cohorts/index.js b/app/assets/javascripts/pages/admin/cohorts/index.js
index 42ef9d38ef7..2d5020dbef4 100644
--- a/app/assets/javascripts/pages/admin/cohorts/index.js
+++ b/app/assets/javascripts/pages/admin/cohorts/index.js
@@ -1,3 +1,3 @@
import initUsagePing from './usage_ping';
-export default () => initUsagePing();
+document.addEventListener('DOMContentLoaded', initUsagePing);
diff --git a/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js b/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js
index 6e66ef69fe1..c1056537f90 100644
--- a/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js
+++ b/app/assets/javascripts/pages/admin/conversational_development_index/show/index.js
@@ -1,3 +1,3 @@
-import UserCallout from '../../../../user_callout';
+import UserCallout from '~/user_callout';
-export default () => new UserCallout();
+document.addEventListener('DOMContentLoaded', () => new UserCallout());
diff --git a/app/assets/javascripts/pages/admin/groups/edit/index.js b/app/assets/javascripts/pages/admin/groups/edit/index.js
index ff9ef8d2449..d3d125a1859 100644
--- a/app/assets/javascripts/pages/admin/groups/edit/index.js
+++ b/app/assets/javascripts/pages/admin/groups/edit/index.js
@@ -1,3 +1,3 @@
-import groupAvatar from '../../../../group_avatar';
+import groupAvatar from '~/group_avatar';
-export default () => groupAvatar();
+document.addEventListener('DOMContentLoaded', groupAvatar);
diff --git a/app/assets/javascripts/pages/admin/groups/new/index.js b/app/assets/javascripts/pages/admin/groups/new/index.js
index fb5c46e4729..21f1ce222ac 100644
--- a/app/assets/javascripts/pages/admin/groups/new/index.js
+++ b/app/assets/javascripts/pages/admin/groups/new/index.js
@@ -2,8 +2,8 @@ import BindInOut from '../../../../behaviors/bind_in_out';
import Group from '../../../../group';
import groupAvatar from '../../../../group_avatar';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
BindInOut.initAll();
new Group(); // eslint-disable-line no-new
groupAvatar();
-};
+});
diff --git a/app/assets/javascripts/pages/admin/groups/show/index.js b/app/assets/javascripts/pages/admin/groups/show/index.js
index 5defea104d4..b0cdad627a6 100644
--- a/app/assets/javascripts/pages/admin/groups/show/index.js
+++ b/app/assets/javascripts/pages/admin/groups/show/index.js
@@ -1,3 +1,3 @@
import UsersSelect from '../../../../users_select';
-export default () => new UsersSelect();
+document.addEventListener('DOMContentLoaded', () => new UsersSelect());
diff --git a/app/assets/javascripts/pages/admin/impersonation_tokens/index.js b/app/assets/javascripts/pages/admin/impersonation_tokens/index.js
index 030328a1363..78a5c4c27be 100644
--- a/app/assets/javascripts/pages/admin/impersonation_tokens/index.js
+++ b/app/assets/javascripts/pages/admin/impersonation_tokens/index.js
@@ -1,3 +1,3 @@
-import DueDateSelectors from '../../../due_date_select';
+import DueDateSelectors from '~/due_date_select';
-export default () => new DueDateSelectors();
+document.addEventListener('DOMContentLoaded', () => new DueDateSelectors());
diff --git a/app/assets/javascripts/pages/admin/index.js b/app/assets/javascripts/pages/admin/index.js
index 8b843037d85..e50b61f09e2 100644
--- a/app/assets/javascripts/pages/admin/index.js
+++ b/app/assets/javascripts/pages/admin/index.js
@@ -1,3 +1,3 @@
import initAdmin from './admin';
-export default () => initAdmin();
+document.addEventListener('DOMContentLoaded', initAdmin);
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
index 555725cbe12..ba1d8e4d8db 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
@@ -1,13 +1,13 @@
<script>
import axios from '~/lib/utils/axios_utils';
- import Flash from '~/flash';
- import modal from '~/vue_shared/components/modal.vue';
- import { s__ } from '~/locale';
+ import createFlash from '~/flash';
+ import GlModal from '~/vue_shared/components/gl_modal.vue';
import { redirectTo } from '~/lib/utils/url_utility';
+ import { s__ } from '~/locale';
export default {
components: {
- modal,
+ GlModal,
},
props: {
url: {
@@ -17,7 +17,7 @@
},
computed: {
text() {
- return s__('AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.');
+ return s__('AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running.');
},
},
methods: {
@@ -28,7 +28,7 @@
redirectTo(response.request.responseURL);
})
.catch((error) => {
- Flash(s__('AdminArea|Stopping jobs failed'));
+ createFlash(s__('AdminArea|Stopping jobs failed'));
throw error;
});
},
@@ -37,11 +37,13 @@
</script>
<template>
- <modal
+ <gl-modal
id="stop-jobs-modal"
- :title="s__('AdminArea|Stop all jobs?')"
- :text="text"
- kind="danger"
- :primary-button-label="s__('AdminArea|Stop jobs')"
- @submit="onSubmit" />
+ :header-title-text="s__('AdminArea|Stop all jobs?')"
+ footer-primary-button-variant="danger"
+ :footer-primary-button-text="s__('AdminArea|Stop jobs')"
+ @submit="onSubmit"
+ >
+ {{ text }}
+ </gl-modal>
</template>
diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js
index 0e004bd9174..5a4f8c6e745 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/index.js
+++ b/app/assets/javascripts/pages/admin/jobs/index/index.js
@@ -1,29 +1,28 @@
import Vue from 'vue';
-
import Translate from '~/vue_shared/translate';
-
import stopJobsModal from './components/stop_jobs_modal.vue';
Vue.use(Translate);
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
const stopJobsButton = document.getElementById('stop-jobs-button');
-
- // eslint-disable-next-line no-new
- new Vue({
- el: '#stop-jobs-modal',
- components: {
- stopJobsModal,
- },
- mounted() {
- stopJobsButton.classList.remove('disabled');
- },
- render(createElement) {
- return createElement('stop-jobs-modal', {
- props: {
- url: stopJobsButton.dataset.url,
- },
- });
- },
- });
-};
+ if (stopJobsButton) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: '#stop-jobs-modal',
+ components: {
+ stopJobsModal,
+ },
+ mounted() {
+ stopJobsButton.classList.remove('disabled');
+ },
+ render(createElement) {
+ return createElement('stop-jobs-modal', {
+ props: {
+ url: stopJobsButton.dataset.url,
+ },
+ });
+ },
+ });
+ }
+});
diff --git a/app/assets/javascripts/pages/admin/labels/edit/index.js b/app/assets/javascripts/pages/admin/labels/edit/index.js
index d7ec6e47f67..5de1d4d6344 100644
--- a/app/assets/javascripts/pages/admin/labels/edit/index.js
+++ b/app/assets/javascripts/pages/admin/labels/edit/index.js
@@ -1,3 +1,3 @@
import Labels from '../../../../labels';
-export default () => new Labels();
+document.addEventListener('DOMContentLoaded', () => new Labels());
diff --git a/app/assets/javascripts/pages/admin/labels/new/index.js b/app/assets/javascripts/pages/admin/labels/new/index.js
index d7ec6e47f67..5de1d4d6344 100644
--- a/app/assets/javascripts/pages/admin/labels/new/index.js
+++ b/app/assets/javascripts/pages/admin/labels/new/index.js
@@ -1,3 +1,3 @@
import Labels from '../../../../labels';
-export default () => new Labels();
+document.addEventListener('DOMContentLoaded', () => new Labels());
diff --git a/app/assets/javascripts/pages/admin/projects/index.js b/app/assets/javascripts/pages/admin/projects/index.js
index 71e0ddcd7b6..31c96eb87af 100644
--- a/app/assets/javascripts/pages/admin/projects/index.js
+++ b/app/assets/javascripts/pages/admin/projects/index.js
@@ -1,9 +1,9 @@
import ProjectsList from '../../../projects_list';
import NamespaceSelect from '../../../namespace_select';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ProjectsList(); // eslint-disable-line no-new
document.querySelectorAll('.js-namespace-select')
.forEach(dropdown => new NamespaceSelect({ dropdown }));
-};
+});
diff --git a/app/assets/javascripts/pages/admin/projects/index/index.js b/app/assets/javascripts/pages/admin/projects/index/index.js
index a87b27090a8..3c597a1093e 100644
--- a/app/assets/javascripts/pages/admin/projects/index/index.js
+++ b/app/assets/javascripts/pages/admin/projects/index/index.js
@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteProjectModal from './components/delete_project_modal.vue';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate);
const deleteProjectModalEl = document.getElementById('delete-project-modal');
@@ -34,4 +34,4 @@ export default () => {
deleteModal.projectName = buttonProps.projectName;
}
});
-};
+});
diff --git a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
index 7b5e333011e..7b5e333011e 100644
--- a/app/assets/javascripts/pages/admin/users/shared/components/delete_user_modal.vue
+++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
diff --git a/app/assets/javascripts/pages/admin/users/shared/index.js b/app/assets/javascripts/pages/admin/users/index.js
index d2a0f82fa2b..4f5d6b55031 100644
--- a/app/assets/javascripts/pages/admin/users/shared/index.js
+++ b/app/assets/javascripts/pages/admin/users/index.js
@@ -5,7 +5,7 @@ import csrf from '~/lib/utils/csrf';
import deleteUserModal from './components/delete_user_modal.vue';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
Vue.use(Translate);
const deleteUserModalEl = document.getElementById('delete-user-modal');
@@ -40,4 +40,4 @@ export default () => {
deleteModal.username = buttonProps.username;
}
});
-};
+});
diff --git a/app/assets/javascripts/pages/ci/lints/ci_lint_editor.js b/app/assets/javascripts/pages/ci/lints/ci_lint_editor.js
index b9469e5b7cb..9ab73be80a0 100644
--- a/app/assets/javascripts/pages/ci/lints/ci_lint_editor.js
+++ b/app/assets/javascripts/pages/ci/lints/ci_lint_editor.js
@@ -2,11 +2,18 @@ export default class CILintEditor {
constructor() {
this.editor = window.ace.edit('ci-editor');
this.textarea = document.querySelector('#content');
+ this.clearYml = document.querySelector('.clear-yml');
this.editor.getSession().setMode('ace/mode/yaml');
this.editor.on('input', () => {
const content = this.editor.getSession().getValue();
this.textarea.value = content;
});
+
+ this.clearYml.addEventListener('click', this.clear.bind(this));
+ }
+
+ clear() {
+ this.editor.setValue('');
}
}
diff --git a/app/assets/javascripts/pages/ci/lints/create/index.js b/app/assets/javascripts/pages/ci/lints/create/index.js
new file mode 100644
index 00000000000..8e8a843da0b
--- /dev/null
+++ b/app/assets/javascripts/pages/ci/lints/create/index.js
@@ -0,0 +1,3 @@
+import CILintEditor from '../ci_lint_editor';
+
+document.addEventListener('DOMContentLoaded', () => new CILintEditor());
diff --git a/app/assets/javascripts/pages/ci/lints/index.js b/app/assets/javascripts/pages/ci/lints/index.js
deleted file mode 100644
index 5cc66546109..00000000000
--- a/app/assets/javascripts/pages/ci/lints/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import CILintEditor from './ci_lint_editor';
-
-export default () => new CILintEditor();
diff --git a/app/assets/javascripts/pages/ci/lints/show/index.js b/app/assets/javascripts/pages/ci/lints/show/index.js
new file mode 100644
index 00000000000..8e8a843da0b
--- /dev/null
+++ b/app/assets/javascripts/pages/ci/lints/show/index.js
@@ -0,0 +1,3 @@
+import CILintEditor from '../ci_lint_editor';
+
+document.addEventListener('DOMContentLoaded', () => new CILintEditor());
diff --git a/app/assets/javascripts/pages/dashboard/activity/index.js b/app/assets/javascripts/pages/dashboard/activity/index.js
index 95faf1f1e98..1b887cad496 100644
--- a/app/assets/javascripts/pages/dashboard/activity/index.js
+++ b/app/assets/javascripts/pages/dashboard/activity/index.js
@@ -1,3 +1,3 @@
import Activities from '~/activities';
-export default () => new Activities();
+document.addEventListener('DOMContentLoaded', () => new Activities());
diff --git a/app/assets/javascripts/pages/dashboard/groups/index/index.js b/app/assets/javascripts/pages/dashboard/groups/index/index.js
index 8a2aae706c0..79987642796 100644
--- a/app/assets/javascripts/pages/dashboard/groups/index/index.js
+++ b/app/assets/javascripts/pages/dashboard/groups/index/index.js
@@ -1,5 +1,3 @@
-import initGroupsList from '../../../../groups';
+import initGroupsList from '~/groups';
-export default () => {
- initGroupsList();
-};
+document.addEventListener('DOMContentLoaded', initGroupsList);
diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js
index b7353669e65..c4901dd1cb6 100644
--- a/app/assets/javascripts/pages/dashboard/issues/index.js
+++ b/app/assets/javascripts/pages/dashboard/issues/index.js
@@ -1,7 +1,7 @@
import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
projectSelect();
initLegacyFilters();
-};
+});
diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
index b7353669e65..c4901dd1cb6 100644
--- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js
+++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
@@ -1,7 +1,7 @@
import projectSelect from '~/project_select';
import initLegacyFilters from '~/init_legacy_filters';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
projectSelect();
initLegacyFilters();
-};
+});
diff --git a/app/assets/javascripts/pages/dashboard/milestones/show/index.js b/app/assets/javascripts/pages/dashboard/milestones/show/index.js
index 2e7a08a369c..397149aaa9e 100644
--- a/app/assets/javascripts/pages/dashboard/milestones/show/index.js
+++ b/app/assets/javascripts/pages/dashboard/milestones/show/index.js
@@ -1,7 +1,9 @@
import Milestone from '~/milestone';
import Sidebar from '~/right_sidebar';
+import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new Milestone(); // eslint-disable-line no-new
new Sidebar(); // eslint-disable-line no-new
-};
+ new MountMilestoneSidebar(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/pages/dashboard/projects/index.js b/app/assets/javascripts/pages/dashboard/projects/index.js
index c88cbf1a6ba..0c585e162cb 100644
--- a/app/assets/javascripts/pages/dashboard/projects/index.js
+++ b/app/assets/javascripts/pages/dashboard/projects/index.js
@@ -1,3 +1,3 @@
import ProjectsList from '~/projects_list';
-export default () => new ProjectsList();
+document.addEventListener('DOMContentLoaded', () => new ProjectsList());
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/index.js b/app/assets/javascripts/pages/dashboard/todos/index/index.js
index 77c23685943..9d2c2f2994f 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/index.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/index.js
@@ -1,3 +1,3 @@
import Todos from './todos';
-export default () => new Todos();
+document.addEventListener('DOMContentLoaded', () => new Todos());
diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js
index e59c38b8bc4..3c7edbdd7c7 100644
--- a/app/assets/javascripts/pages/explore/groups/index.js
+++ b/app/assets/javascripts/pages/explore/groups/index.js
@@ -2,7 +2,7 @@ import GroupsList from '~/groups_list';
import Landing from '~/landing';
import initGroupsList from '../../../groups';
-export default function () {
+document.addEventListener('DOMContentLoaded', () => {
new GroupsList(); // eslint-disable-line no-new
initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing');
@@ -13,4 +13,4 @@ export default function () {
'explore_groups_landing_dismissed',
);
exploreGroupsLanding.toggle();
-}
+});
diff --git a/app/assets/javascripts/pages/explore/projects/index.js b/app/assets/javascripts/pages/explore/projects/index.js
index c88cbf1a6ba..0c585e162cb 100644
--- a/app/assets/javascripts/pages/explore/projects/index.js
+++ b/app/assets/javascripts/pages/explore/projects/index.js
@@ -1,3 +1,3 @@
import ProjectsList from '~/projects_list';
-export default () => new ProjectsList();
+document.addEventListener('DOMContentLoaded', () => new ProjectsList());
diff --git a/app/assets/javascripts/pages/groups/activity/index.js b/app/assets/javascripts/pages/groups/activity/index.js
index 95faf1f1e98..1b887cad496 100644
--- a/app/assets/javascripts/pages/groups/activity/index.js
+++ b/app/assets/javascripts/pages/groups/activity/index.js
@@ -1,3 +1,3 @@
import Activities from '~/activities';
-export default () => new Activities();
+document.addEventListener('DOMContentLoaded', () => new Activities());
diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js
index 1aeec55a4be..d44874c8741 100644
--- a/app/assets/javascripts/pages/groups/edit/index.js
+++ b/app/assets/javascripts/pages/groups/edit/index.js
@@ -1,7 +1,7 @@
import groupAvatar from '~/group_avatar';
import TransferDropdown from '~/groups/transfer_dropdown';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
groupAvatar();
new TransferDropdown(); // eslint-disable-line no-new
-};
+});
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 29319b97ae2..c22a164cd4e 100644
--- a/app/assets/javascripts/pages/groups/group_members/index/index.js
+++ b/app/assets/javascripts/pages/groups/group_members/index/index.js
@@ -4,8 +4,8 @@ import memberExpirationDate from '~/member_expiration_date';
import Members from '~/members';
import UsersSelect from '~/users_select';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
memberExpirationDate();
new Members();
new UsersSelect();
-};
+});
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index fbdfabd1e95..d149b307e7f 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
});
projectSelect();
-};
+});
diff --git a/app/assets/javascripts/pages/groups/labels/edit/index.js b/app/assets/javascripts/pages/groups/labels/edit/index.js
index 72c5e4744ac..fa81ad914ba 100644
--- a/app/assets/javascripts/pages/groups/labels/edit/index.js
+++ b/app/assets/javascripts/pages/groups/labels/edit/index.js
@@ -1,3 +1,3 @@
import Labels from '~/labels';
-export default () => new Labels();
+document.addEventListener('DOMContentLoaded', () => new Labels());
diff --git a/app/assets/javascripts/pages/groups/labels/index/index.js b/app/assets/javascripts/pages/groups/labels/index/index.js
index 018345fa112..6e45de2a724 100644
--- a/app/assets/javascripts/pages/groups/labels/index/index.js
+++ b/app/assets/javascripts/pages/groups/labels/index/index.js
@@ -1,3 +1,3 @@
import initLabels from '~/init_labels';
-export default initLabels;
+document.addEventListener('DOMContentLoaded', initLabels);
diff --git a/app/assets/javascripts/pages/groups/labels/new/index.js b/app/assets/javascripts/pages/groups/labels/new/index.js
index 72c5e4744ac..fa81ad914ba 100644
--- a/app/assets/javascripts/pages/groups/labels/new/index.js
+++ b/app/assets/javascripts/pages/groups/labels/new/index.js
@@ -1,3 +1,3 @@
import Labels from '~/labels';
-export default () => new Labels();
+document.addEventListener('DOMContentLoaded', () => new Labels());
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index f6d284bf9ef..a5cc1f34b63 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -2,9 +2,9 @@ import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
});
projectSelect();
-};
+});
diff --git a/app/assets/javascripts/pages/groups/milestones/edit/index.js b/app/assets/javascripts/pages/groups/milestones/edit/index.js
index 5c99c90e24d..ddd10fe5062 100644
--- a/app/assets/javascripts/pages/groups/milestones/edit/index.js
+++ b/app/assets/javascripts/pages/groups/milestones/edit/index.js
@@ -1,3 +1,3 @@
import initForm from '../../../../shared/milestones/form';
-export default () => initForm(false);
+document.addEventListener('DOMContentLoaded', () => initForm(false));
diff --git a/app/assets/javascripts/pages/groups/milestones/new/index.js b/app/assets/javascripts/pages/groups/milestones/new/index.js
index 5c99c90e24d..ddd10fe5062 100644
--- a/app/assets/javascripts/pages/groups/milestones/new/index.js
+++ b/app/assets/javascripts/pages/groups/milestones/new/index.js
@@ -1,3 +1,3 @@
import initForm from '../../../../shared/milestones/form';
-export default () => initForm(false);
+document.addEventListener('DOMContentLoaded', () => initForm(false));
diff --git a/app/assets/javascripts/pages/groups/milestones/show/index.js b/app/assets/javascripts/pages/groups/milestones/show/index.js
index c9a18353f2e..88f40b5278e 100644
--- a/app/assets/javascripts/pages/groups/milestones/show/index.js
+++ b/app/assets/javascripts/pages/groups/milestones/show/index.js
@@ -1,3 +1,3 @@
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
-export default initMilestonesShow;
+document.addEventListener('DOMContentLoaded', initMilestonesShow);
diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js
index 7850b90d3d2..b2f275dc5ea 100644
--- a/app/assets/javascripts/pages/groups/new/index.js
+++ b/app/assets/javascripts/pages/groups/new/index.js
@@ -2,8 +2,8 @@ import BindInOut from '~/behaviors/bind_in_out';
import Group from '~/group';
import groupAvatar from '~/group_avatar';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
BindInOut.initAll();
new Group(); // eslint-disable-line no-new
groupAvatar();
-};
+});
diff --git a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
index ad79f7e09ac..04a0d8117cc 100644
--- a/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/groups/settings/ci_cd/show/index.js
@@ -1,6 +1,6 @@
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
const variableListEl = document.querySelector('.js-ci-variable-list-section');
// eslint-disable-next-line no-new
new AjaxVariableList({
@@ -9,4 +9,4 @@ export default () => {
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
-};
+});
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
index 5c763986da3..d7b35d2b26b 100644
--- a/app/assets/javascripts/pages/groups/show/index.js
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -5,7 +5,7 @@ import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/shortcuts_navigation';
-import initGroupsList from '../../../groups';
+import initGroupsList from '~/groups';
document.addEventListener('DOMContentLoaded', () => {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
diff --git a/app/assets/javascripts/pages/help/index.js b/app/assets/javascripts/pages/help/index.js
deleted file mode 100644
index 4cf8afc4b7e..00000000000
--- a/app/assets/javascripts/pages/help/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import VersionCheckImage from '../../version_check_image';
-
-export default () => VersionCheckImage.bindErrorEvent($('img.js-version-status-badge'));
diff --git a/app/assets/javascripts/pages/help/index/index.js b/app/assets/javascripts/pages/help/index/index.js
new file mode 100644
index 00000000000..05c81fc618b
--- /dev/null
+++ b/app/assets/javascripts/pages/help/index/index.js
@@ -0,0 +1,7 @@
+import VersionCheckImage from '~/version_check_image';
+import docs from '~/docs/docs_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ docs();
+ VersionCheckImage.bindErrorEvent($('img.js-version-status-badge'));
+});
diff --git a/app/assets/javascripts/pages/help/show/index.js b/app/assets/javascripts/pages/help/show/index.js
new file mode 100644
index 00000000000..ec426a850b6
--- /dev/null
+++ b/app/assets/javascripts/pages/help/show/index.js
@@ -0,0 +1,3 @@
+import initHelp from '~/help/help';
+
+document.addEventListener('DOMContentLoaded', initHelp);
diff --git a/app/assets/javascripts/pages/help/ui/index.js b/app/assets/javascripts/pages/help/ui/index.js
new file mode 100644
index 00000000000..709ca2f3828
--- /dev/null
+++ b/app/assets/javascripts/pages/help/ui/index.js
@@ -0,0 +1,3 @@
+import initUIKit from '~/ui_development_kit';
+
+document.addEventListener('DOMContentLoaded', initUIKit);
diff --git a/app/assets/javascripts/pages/import/fogbugz/new_user_map/index.js b/app/assets/javascripts/pages/import/fogbugz/new_user_map/index.js
index 5defea104d4..68d4c1f049f 100644
--- a/app/assets/javascripts/pages/import/fogbugz/new_user_map/index.js
+++ b/app/assets/javascripts/pages/import/fogbugz/new_user_map/index.js
@@ -1,3 +1,3 @@
-import UsersSelect from '../../../../users_select';
+import UsersSelect from '~/users_select';
-export default () => new UsersSelect();
+document.addEventListener('DOMContentLoaded', () => new UsersSelect());
diff --git a/app/assets/javascripts/pages/import/gitlab_projects/new/index.js b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js
new file mode 100644
index 00000000000..bb86f72b95b
--- /dev/null
+++ b/app/assets/javascripts/pages/import/gitlab_projects/new/index.js
@@ -0,0 +1,3 @@
+import initGitLabImportProject from '~/projects/project_import_gitlab_project';
+
+document.addEventListener('DOMContentLoaded', initGitLabImportProject);
diff --git a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js
index 7aa5be0d5b9..b2a896a3265 100644
--- a/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js
+++ b/app/assets/javascripts/pages/milestones/shared/init_milestones_show.js
@@ -2,8 +2,10 @@
import Milestone from '~/milestone';
import Sidebar from '~/right_sidebar';
+import MountMilestoneSidebar from '~/sidebar/mount_milestone_sidebar';
export default () => {
new Milestone();
new Sidebar();
+ new MountMilestoneSidebar();
};
diff --git a/app/assets/javascripts/pages/omniauth_callbacks/index.js b/app/assets/javascripts/pages/omniauth_callbacks/index.js
index 54f4e56359a..c2c069d1ca8 100644
--- a/app/assets/javascripts/pages/omniauth_callbacks/index.js
+++ b/app/assets/javascripts/pages/omniauth_callbacks/index.js
@@ -1,5 +1,3 @@
import initU2F from '../../shared/sessions/u2f';
-export default () => {
- initU2F();
-};
+document.addEventListener('DOMContentLoaded', initU2F);
diff --git a/app/assets/javascripts/pages/profiles/accounts/show/index.js b/app/assets/javascripts/pages/profiles/accounts/show/index.js
new file mode 100644
index 00000000000..96c3d725780
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/accounts/show/index.js
@@ -0,0 +1,3 @@
+import initProfileAccount from '~/profile/account';
+
+document.addEventListener('DOMContentLoaded', initProfileAccount);
diff --git a/app/assets/javascripts/pages/profiles/index/index.js b/app/assets/javascripts/pages/profiles/index/index.js
index 90eed38777a..9bd430f4f11 100644
--- a/app/assets/javascripts/pages/profiles/index/index.js
+++ b/app/assets/javascripts/pages/profiles/index/index.js
@@ -1,7 +1,7 @@
import NotificationsForm from '../../../notifications_form';
import notificationsDropdown from '../../../notifications_dropdown';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new NotificationsForm(); // eslint-disable-line no-new
notificationsDropdown();
-};
+});
diff --git a/app/assets/javascripts/pages/profiles/personal_access_tokens/index.js b/app/assets/javascripts/pages/profiles/personal_access_tokens/index.js
index 030328a1363..78a5c4c27be 100644
--- a/app/assets/javascripts/pages/profiles/personal_access_tokens/index.js
+++ b/app/assets/javascripts/pages/profiles/personal_access_tokens/index.js
@@ -1,3 +1,3 @@
-import DueDateSelectors from '../../../due_date_select';
+import DueDateSelectors from '~/due_date_select';
-export default () => new DueDateSelectors();
+document.addEventListener('DOMContentLoaded', () => new DueDateSelectors());
diff --git a/app/assets/javascripts/pages/projects/activity/index.js b/app/assets/javascripts/pages/projects/activity/index.js
index 7af95127fd5..5543ad82428 100644
--- a/app/assets/javascripts/pages/projects/activity/index.js
+++ b/app/assets/javascripts/pages/projects/activity/index.js
@@ -1,7 +1,7 @@
import Activities from '~/activities';
import ShortcutsNavigation from '~/shortcuts_navigation';
-export default function () {
+document.addEventListener('DOMContentLoaded', () => {
new Activities(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
-}
+});
diff --git a/app/assets/javascripts/pages/projects/artifacts/browse/index.js b/app/assets/javascripts/pages/projects/artifacts/browse/index.js
index 02456071086..ea7458fe9b8 100644
--- a/app/assets/javascripts/pages/projects/artifacts/browse/index.js
+++ b/app/assets/javascripts/pages/projects/artifacts/browse/index.js
@@ -1,7 +1,7 @@
import BuildArtifacts from '~/build_artifacts';
import ShortcutsNavigation from '~/shortcuts_navigation';
-export default function () {
+document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new BuildArtifacts(); // eslint-disable-line no-new
-}
+});
diff --git a/app/assets/javascripts/pages/projects/artifacts/file/index.js b/app/assets/javascripts/pages/projects/artifacts/file/index.js
index 4cd67ac76e3..8484e5e9848 100644
--- a/app/assets/javascripts/pages/projects/artifacts/file/index.js
+++ b/app/assets/javascripts/pages/projects/artifacts/file/index.js
@@ -1,7 +1,7 @@
import BlobViewer from '~/blob/viewer/index';
import ShortcutsNavigation from '~/shortcuts_navigation';
-export default function () {
+document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
-}
+});
diff --git a/app/assets/javascripts/pages/projects/blame/show/index.js b/app/assets/javascripts/pages/projects/blame/show/index.js
index 480357a309c..80d0bff92fa 100644
--- a/app/assets/javascripts/pages/projects/blame/show/index.js
+++ b/app/assets/javascripts/pages/projects/blame/show/index.js
@@ -1,3 +1,3 @@
import initBlob from '~/pages/projects/init_blob';
-export default initBlob;
+document.addEventListener('DOMContentLoaded', initBlob);
diff --git a/app/assets/javascripts/pages/projects/blob/edit/index.js b/app/assets/javascripts/pages/projects/blob/edit/index.js
new file mode 100644
index 00000000000..189053f3ed7
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/blob/edit/index.js
@@ -0,0 +1,3 @@
+import initBlobBundle from '~/blob_edit/blob_bundle';
+
+document.addEventListener('DOMContentLoaded', initBlobBundle);
diff --git a/app/assets/javascripts/pages/projects/blob/new/index.js b/app/assets/javascripts/pages/projects/blob/new/index.js
new file mode 100644
index 00000000000..189053f3ed7
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/blob/new/index.js
@@ -0,0 +1,3 @@
+import initBlobBundle from '~/blob_edit/blob_bundle';
+
+document.addEventListener('DOMContentLoaded', initBlobBundle);
diff --git a/app/assets/javascripts/pages/projects/blob/show/index.js b/app/assets/javascripts/pages/projects/blob/show/index.js
index a3eeb1cefb6..26cbb279d4a 100644
--- a/app/assets/javascripts/pages/projects/blob/show/index.js
+++ b/app/assets/javascripts/pages/projects/blob/show/index.js
@@ -1,7 +1,7 @@
import BlobViewer from '~/blob/viewer/index';
import initBlob from '~/pages/projects/init_blob';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new BlobViewer(); // eslint-disable-line no-new
initBlob();
-};
+});
diff --git a/app/assets/javascripts/pages/projects/boards/index.js b/app/assets/javascripts/pages/projects/boards/index.js
index 3aeeedbb45d..5cfe8723204 100644
--- a/app/assets/javascripts/pages/projects/boards/index.js
+++ b/app/assets/javascripts/pages/projects/boards/index.js
@@ -1,7 +1,9 @@
import UsersSelect from '~/users_select';
import ShortcutsNavigation from '~/shortcuts_navigation';
+import initBoards from '~/boards';
document.addEventListener('DOMContentLoaded', () => {
new UsersSelect(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
+ initBoards();
});
diff --git a/app/assets/javascripts/pages/projects/branches/index/index.js b/app/assets/javascripts/pages/projects/branches/index/index.js
index cee0f19bf2a..8fa266a37ce 100644
--- a/app/assets/javascripts/pages/projects/branches/index/index.js
+++ b/app/assets/javascripts/pages/projects/branches/index/index.js
@@ -1,7 +1,7 @@
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
import DeleteModal from '~/branches/branches_delete_modal';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
AjaxLoadingSpinner.init();
new DeleteModal(); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js
index ae5e033e97e..d32d5c6cb29 100644
--- a/app/assets/javascripts/pages/projects/branches/new/index.js
+++ b/app/assets/javascripts/pages/projects/branches/new/index.js
@@ -1,3 +1,5 @@
import NewBranchForm from '~/new_branch_form';
-export default () => new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML));
+document.addEventListener('DOMContentLoaded', () => (
+ new NewBranchForm($('.js-create-branch-form'), JSON.parse(document.getElementById('availableRefs').innerHTML))
+));
diff --git a/app/assets/javascripts/pages/projects/clusters/destroy/index.js b/app/assets/javascripts/pages/projects/clusters/destroy/index.js
new file mode 100644
index 00000000000..8001d2dd1da
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/clusters/destroy/index.js
@@ -0,0 +1,5 @@
+import ClustersBundle from '~/clusters/clusters_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new ClustersBundle(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js
index d531ab81dc7..e4b8baede58 100644
--- a/app/assets/javascripts/pages/projects/clusters/index/index.js
+++ b/app/assets/javascripts/pages/projects/clusters/index/index.js
@@ -1,5 +1,5 @@
import ClustersIndex from '~/clusters/clusters_index';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ClustersIndex(); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/clusters/show/index.js b/app/assets/javascripts/pages/projects/clusters/show/index.js
index 0458c02a66f..8001d2dd1da 100644
--- a/app/assets/javascripts/pages/projects/clusters/show/index.js
+++ b/app/assets/javascripts/pages/projects/clusters/show/index.js
@@ -1,5 +1,5 @@
import ClustersBundle from '~/clusters/clusters_bundle';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ClustersBundle(); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/clusters/update/index.js b/app/assets/javascripts/pages/projects/clusters/update/index.js
new file mode 100644
index 00000000000..8001d2dd1da
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/clusters/update/index.js
@@ -0,0 +1,5 @@
+import ClustersBundle from '~/clusters/clusters_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new ClustersBundle(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/pages/projects/commit/pipelines/index.js b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
index 523ad567021..cd923f13ce8 100644
--- a/app/assets/javascripts/pages/projects/commit/pipelines/index.js
+++ b/app/assets/javascripts/pages/projects/commit/pipelines/index.js
@@ -1,8 +1,10 @@
import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
+import initPipelines from '~/commit/pipelines/pipelines_bundle';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new MiniPipelineGraph({
container: '.js-commit-pipeline-graph',
}).bindEvents();
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
-};
+ initPipelines();
+});
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 5ac38e6f278..460a54ab504 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -7,7 +7,7 @@ import initNotes from '~/init_notes';
import initChangesDropdown from '~/init_changes_dropdown';
import { fetchCommitMergeRequests } from '~/commit_merge_requests';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new Diff();
new ZenMode();
new ShortcutsNavigation();
@@ -19,4 +19,4 @@ export default () => {
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - stickyBarPaddingTop);
$('.commit-info.branches').load(document.querySelector('.js-commit-box').dataset.commitPath);
fetchCommitMergeRequests();
-};
+});
diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js
index 90b5882a24f..3682020579b 100644
--- a/app/assets/javascripts/pages/projects/commits/show/index.js
+++ b/app/assets/javascripts/pages/projects/commits/show/index.js
@@ -2,8 +2,8 @@ import CommitsList from '~/commits';
import GpgBadges from '~/gpg_badges';
import ShortcutsNavigation from '~/shortcuts_navigation';
-export default () => {
- CommitsList.init(document.querySelector('.js-project-commits-show').dataset.commitsLimit);
+document.addEventListener('DOMContentLoaded', () => {
+ new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
GpgBadges.fetch();
-};
+});
diff --git a/app/assets/javascripts/pages/projects/compare/index.js b/app/assets/javascripts/pages/projects/compare/index.js
index 890062eeee6..d1c78bd61db 100644
--- a/app/assets/javascripts/pages/projects/compare/index.js
+++ b/app/assets/javascripts/pages/projects/compare/index.js
@@ -1,5 +1,3 @@
import initCompareAutocomplete from '~/compare_autocomplete';
-export default () => {
- initCompareAutocomplete();
-};
+document.addEventListener('DOMContentLoaded', initCompareAutocomplete);
diff --git a/app/assets/javascripts/pages/projects/compare/show/index.js b/app/assets/javascripts/pages/projects/compare/show/index.js
index 6b8d4503568..2b4fd3c47c0 100644
--- a/app/assets/javascripts/pages/projects/compare/show/index.js
+++ b/app/assets/javascripts/pages/projects/compare/show/index.js
@@ -1,8 +1,8 @@
import Diff from '~/diff';
import initChangesDropdown from '~/init_changes_dropdown';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new Diff(); // eslint-disable-line no-new
const paddingTop = 16;
initChangesDropdown(document.querySelector('.navbar-gitlab').offsetHeight - paddingTop);
-};
+});
diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js
index 9edf36d66b1..064de22dfd6 100644
--- a/app/assets/javascripts/pages/projects/edit/index.js
+++ b/app/assets/javascripts/pages/projects/edit/index.js
@@ -4,11 +4,11 @@ import ProjectNew from '../shared/project_new';
import projectAvatar from '../shared/project_avatar';
import initProjectPermissionsSettings from '../shared/permissions';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ProjectNew(); // eslint-disable-line no-new
setupProjectEdit();
// Initialize expandable settings panels
initSettingsPanels();
projectAvatar();
initProjectPermissionsSettings();
-};
+});
diff --git a/app/assets/javascripts/pages/projects/environments/folder/index.js b/app/assets/javascripts/pages/projects/environments/folder/index.js
new file mode 100644
index 00000000000..5feaf944038
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/environments/folder/index.js
@@ -0,0 +1,3 @@
+import initEnvironmentsFolderBundle from '~/environments/folder/environments_folder_bundle';
+
+document.addEventListener('DOMContentLoaded', initEnvironmentsFolderBundle);
diff --git a/app/assets/javascripts/pages/projects/environments/metrics/index.js b/app/assets/javascripts/pages/projects/environments/metrics/index.js
index f4760cb2720..0b644780ad4 100644
--- a/app/assets/javascripts/pages/projects/environments/metrics/index.js
+++ b/app/assets/javascripts/pages/projects/environments/metrics/index.js
@@ -1,3 +1,3 @@
import monitoringBundle from '~/monitoring/monitoring_bundle';
-export default monitoringBundle;
+document.addEventListener('DOMContentLoaded', monitoringBundle);
diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js
index 42bde0ff779..23d857d69ec 100644
--- a/app/assets/javascripts/pages/projects/find_file/show/index.js
+++ b/app/assets/javascripts/pages/projects/find_file/show/index.js
@@ -1,7 +1,7 @@
import ProjectFindFile from '~/project_find_file';
import ShortcutsFindFile from '~/shortcuts_find_file';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
const findElement = document.querySelector('.js-file-finder');
const projectFindFile = new ProjectFindFile($('.file-finder-holder'), {
url: findElement.dataset.fileFindUrl,
@@ -9,4 +9,4 @@ export default () => {
blobUrlTemplate: findElement.dataset.blobUrlTemplate,
});
new ShortcutsFindFile(projectFindFile); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/forks/new/index.js b/app/assets/javascripts/pages/projects/forks/new/index.js
index 7825eb01949..d80e27e9156 100644
--- a/app/assets/javascripts/pages/projects/forks/new/index.js
+++ b/app/assets/javascripts/pages/projects/forks/new/index.js
@@ -1,5 +1,3 @@
import ProjectFork from '~/project_fork';
-export default () => {
- new ProjectFork(); // eslint-disable-line no-new
-};
+document.addEventListener('DOMContentLoaded', () => new ProjectFork());
diff --git a/app/assets/javascripts/graphs/graphs_charts.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js
index ec6eab34989..42df19c2968 100644
--- a/app/assets/javascripts/graphs/graphs_charts.js
+++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js
@@ -1,4 +1,4 @@
-import Chart from 'vendor/Chart';
+import Chart from 'chart.js';
import _ from 'underscore';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/graphs/graphs_show.js b/app/assets/javascripts/pages/projects/graphs/show/index.js
index b670e907a5c..f516ff20995 100644
--- a/app/assets/javascripts/graphs/graphs_show.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/index.js
@@ -1,6 +1,6 @@
-import flash from '../flash';
-import { __ } from '../locale';
-import axios from '../lib/utils/axios_utils';
+import flash from '~/flash';
+import { __ } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
import ContributorsStatGraph from './stat_graph_contributors';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
index 151a4ce012c..9ac0b4c07e5 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors.js
@@ -1,9 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, camelcase, one-var-declaration-per-line, quotes, no-param-reassign, quote-props, comma-dangle, prefer-template, max-len, no-return-assign, no-shadow */
import _ from 'underscore';
+import { n__, s__, createDateTimeFormat, sprintf } from '~/locale';
import { ContributorsGraph, ContributorsAuthorGraph, ContributorsMasterGraph } from './stat_graph_contributors_graph';
import ContributorsStatGraphUtil from './stat_graph_contributors_util';
-import { n__, s__, createDateTimeFormat, sprintf } from '../locale';
export default (function() {
function ContributorsStatGraph() {
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
index 9a4012232a0..6ffaa277a0a 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_graph.js
@@ -7,7 +7,7 @@ import { axisLeft, axisBottom } from 'd3-axis';
import { area } from 'd3-shape';
import { brushX } from 'd3-brush';
import { timeParse } from 'd3-time-format';
-import { dateTickFormat } from '../lib/utils/tick_formats';
+import { dateTickFormat } from '~/lib/utils/tick_formats';
const d3 = { extent, max, select, scaleTime, scaleLinear, axisLeft, axisBottom, area, brushX, timeParse };
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_util.js b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
index 77135ad1f0e..77135ad1f0e 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_util.js
+++ b/app/assets/javascripts/pages/projects/graphs/show/stat_graph_contributors_util.js
diff --git a/app/assets/javascripts/pages/projects/imports/show/index.js b/app/assets/javascripts/pages/projects/imports/show/index.js
index 378f7b3f38b..d5f92baf054 100644
--- a/app/assets/javascripts/pages/projects/imports/show/index.js
+++ b/app/assets/javascripts/pages/projects/imports/show/index.js
@@ -1,5 +1,5 @@
import ProjectImport from '~/project_import';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ProjectImport(); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index 9b1d52692a3..de1e13de7e9 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,7 +1,7 @@
import Project from './project';
import ShortcutsNavigation from '../../shortcuts_navigation';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new Project(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/init_blob.js b/app/assets/javascripts/pages/projects/init_blob.js
index 26f0ad46114..82143fa875a 100644
--- a/app/assets/javascripts/pages/projects/init_blob.js
+++ b/app/assets/javascripts/pages/projects/init_blob.js
@@ -3,6 +3,7 @@ import BlobLinePermalinkUpdater from '~/blob/blob_line_permalink_updater';
import ShortcutsNavigation from '~/shortcuts_navigation';
import ShortcutsBlob from '~/shortcuts_blob';
import BlobForkSuggestion from '~/blob/blob_fork_suggestion';
+import initBlobBundle from '~/blob_edit/blob_bundle';
export default () => {
new LineHighlighter(); // eslint-disable-line no-new
@@ -30,4 +31,6 @@ export default () => {
suggestionSections: document.querySelectorAll('.js-file-fork-suggestion-section'),
actionTextPieces: document.querySelectorAll('.js-file-fork-suggestion-section-action'),
}).init();
+
+ initBlobBundle();
};
diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js
index 7f27f379d8c..ffc84dc106b 100644
--- a/app/assets/javascripts/pages/projects/issues/edit/index.js
+++ b/app/assets/javascripts/pages/projects/issues/edit/index.js
@@ -1,5 +1,3 @@
import initForm from '../form';
-export default () => {
- initForm();
-};
+document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js
index 7f27f379d8c..ffc84dc106b 100644
--- a/app/assets/javascripts/pages/projects/issues/new/index.js
+++ b/app/assets/javascripts/pages/projects/issues/new/index.js
@@ -1,5 +1,3 @@
import initForm from '../form';
-export default () => {
- initForm();
-};
+document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pages/projects/issues/show/index.js b/app/assets/javascripts/pages/projects/issues/show/index.js
index da312c1f1b7..1e56aa58da2 100644
--- a/app/assets/javascripts/pages/projects/issues/show/index.js
+++ b/app/assets/javascripts/pages/projects/issues/show/index.js
@@ -1,13 +1,13 @@
-/* eslint-disable no-new */
-
import initIssuableSidebar from '~/init_issuable_sidebar';
import Issue from '~/issue';
import ShortcutsIssuable from '~/shortcuts_issuable';
import ZenMode from '~/zen_mode';
+import '~/notes/index';
+import '~/issue_show/index';
document.addEventListener('DOMContentLoaded', () => {
- new Issue();
- new ShortcutsIssuable();
- new ZenMode();
+ new Issue(); // eslint-disable-line no-new
+ new ShortcutsIssuable(); // eslint-disable-line no-new
+ new ZenMode(); // eslint-disable-line no-new
initIssuableSidebar();
});
diff --git a/app/assets/javascripts/pages/projects/jobs/show/index.js b/app/assets/javascripts/pages/projects/jobs/show/index.js
new file mode 100644
index 00000000000..3626f3ffec6
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/jobs/show/index.js
@@ -0,0 +1,3 @@
+import initJobDetails from '~/jobs/job_details_bundle';
+
+document.addEventListener('DOMContentLoaded', initJobDetails);
diff --git a/app/assets/javascripts/pages/projects/labels/edit/index.js b/app/assets/javascripts/pages/projects/labels/edit/index.js
index 72c5e4744ac..fa81ad914ba 100644
--- a/app/assets/javascripts/pages/projects/labels/edit/index.js
+++ b/app/assets/javascripts/pages/projects/labels/edit/index.js
@@ -1,3 +1,3 @@
import Labels from '~/labels';
-export default () => new Labels();
+document.addEventListener('DOMContentLoaded', () => new Labels());
diff --git a/app/assets/javascripts/pages/projects/labels/index/index.js b/app/assets/javascripts/pages/projects/labels/index/index.js
index 018345fa112..6e45de2a724 100644
--- a/app/assets/javascripts/pages/projects/labels/index/index.js
+++ b/app/assets/javascripts/pages/projects/labels/index/index.js
@@ -1,3 +1,3 @@
import initLabels from '~/init_labels';
-export default initLabels;
+document.addEventListener('DOMContentLoaded', initLabels);
diff --git a/app/assets/javascripts/pages/projects/labels/new/index.js b/app/assets/javascripts/pages/projects/labels/new/index.js
index 72c5e4744ac..fa81ad914ba 100644
--- a/app/assets/javascripts/pages/projects/labels/new/index.js
+++ b/app/assets/javascripts/pages/projects/labels/new/index.js
@@ -1,3 +1,3 @@
import Labels from '~/labels';
-export default () => new Labels();
+document.addEventListener('DOMContentLoaded', () => new Labels());
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js
index 734d01ae6f2..febfecebbd2 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/diffs/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/index.js
@@ -1,3 +1,3 @@
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
-export default initMergeRequest;
+document.addEventListener('DOMContentLoaded', initMergeRequest);
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
index ccd0b54c5ed..6c9afddefac 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
@@ -1,7 +1,8 @@
import Compare from '~/compare';
import MergeRequest from '~/merge_request';
+import initPipelines from '~/commit/pipelines/pipelines_bundle';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) {
new Compare({ // eslint-disable-line no-new
@@ -14,5 +15,6 @@ export default () => {
new MergeRequest({ // eslint-disable-line no-new
action: mrNewSubmitNode.dataset.mrSubmitAction,
});
+ initPipelines();
}
-};
+});
diff --git a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
index 734d01ae6f2..febfecebbd2 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/edit/index.js
@@ -1,3 +1,3 @@
import initMergeRequest from '~/pages/projects/merge_requests/init_merge_request';
-export default initMergeRequest;
+document.addEventListener('DOMContentLoaded', initMergeRequest);
diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
new file mode 100644
index 00000000000..322d02f93db
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
@@ -0,0 +1,30 @@
+import MergeRequest from '~/merge_request';
+import ZenMode from '~/zen_mode';
+import initNotes from '~/init_notes';
+import initIssuableSidebar from '~/init_issuable_sidebar';
+import initDiffNotes from '~/diff_notes/diff_notes_bundle';
+import ShortcutsIssuable from '~/shortcuts_issuable';
+import Diff from '~/diff';
+import { handleLocationHash } from '~/lib/utils/common_utils';
+import howToMerge from '~/how_to_merge';
+import initPipelines from '~/commit/pipelines/pipelines_bundle';
+
+document.addEventListener('DOMContentLoaded', () => {
+ new Diff(); // eslint-disable-line no-new
+ new ZenMode(); // eslint-disable-line no-new
+
+ initIssuableSidebar();
+ initNotes();
+ initDiffNotes();
+ initPipelines();
+
+ const mrShowNode = document.querySelector('.merge-request');
+
+ window.mergeRequest = new MergeRequest({
+ action: mrShowNode.dataset.mrAction,
+ });
+
+ new ShortcutsIssuable(true); // eslint-disable-line no-new
+ handleLocationHash();
+ howToMerge();
+});
diff --git a/app/assets/javascripts/pages/projects/milestones/edit/index.js b/app/assets/javascripts/pages/projects/milestones/edit/index.js
index 10e3979a36e..9a4ebf9890d 100644
--- a/app/assets/javascripts/pages/projects/milestones/edit/index.js
+++ b/app/assets/javascripts/pages/projects/milestones/edit/index.js
@@ -1,3 +1,3 @@
import initForm from '../../../../shared/milestones/form';
-export default () => initForm();
+document.addEventListener('DOMContentLoaded', () => initForm());
diff --git a/app/assets/javascripts/pages/projects/milestones/index/index.js b/app/assets/javascripts/pages/projects/milestones/index/index.js
index 8fb4d83d8a3..38789365a67 100644
--- a/app/assets/javascripts/pages/projects/milestones/index/index.js
+++ b/app/assets/javascripts/pages/projects/milestones/index/index.js
@@ -1,3 +1,3 @@
import milestones from '~/pages/milestones/shared';
-export default milestones;
+document.addEventListener('DOMContentLoaded', milestones);
diff --git a/app/assets/javascripts/pages/projects/milestones/new/index.js b/app/assets/javascripts/pages/projects/milestones/new/index.js
index 10e3979a36e..9a4ebf9890d 100644
--- a/app/assets/javascripts/pages/projects/milestones/new/index.js
+++ b/app/assets/javascripts/pages/projects/milestones/new/index.js
@@ -1,3 +1,3 @@
import initForm from '../../../../shared/milestones/form';
-export default () => initForm();
+document.addEventListener('DOMContentLoaded', () => initForm());
diff --git a/app/assets/javascripts/pages/projects/milestones/show/index.js b/app/assets/javascripts/pages/projects/milestones/show/index.js
index 35b5c9c2ced..84a52421598 100644
--- a/app/assets/javascripts/pages/projects/milestones/show/index.js
+++ b/app/assets/javascripts/pages/projects/milestones/show/index.js
@@ -1,7 +1,7 @@
import initMilestonesShow from '~/pages/milestones/shared/init_milestones_show';
import milestones from '~/pages/milestones/shared';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
initMilestonesShow();
milestones();
-};
+});
diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js
index 71c49deb9d0..ea6fd961393 100644
--- a/app/assets/javascripts/pages/projects/new/index.js
+++ b/app/assets/javascripts/pages/projects/new/index.js
@@ -2,8 +2,8 @@ import ProjectNew from '../shared/project_new';
import initProjectVisibilitySelector from '../../../project_visibility';
import initProjectNew from '../../../projects/project_new';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ProjectNew(); // eslint-disable-line no-new
initProjectVisibilitySelector();
initProjectNew.bindEvents();
-};
+});
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js
new file mode 100644
index 00000000000..d65be6bc69e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/create/index.js
@@ -0,0 +1,3 @@
+import initForm from '../shared/init_form';
+
+document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js
new file mode 100644
index 00000000000..d65be6bc69e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/edit/index.js
@@ -0,0 +1,3 @@
+import initForm from '../shared/init_form';
+
+document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
index a6c945e22b0..544360dcd51 100644
--- a/app/assets/javascripts/pipeline_schedules/pipeline_schedules_index_bundle.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/index/index.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import PipelineSchedulesCallout from './components/pipeline_schedules_callout.vue';
+import PipelineSchedulesCallout from '../shared/components/pipeline_schedules_callout.vue';
document.addEventListener('DOMContentLoaded', () => new Vue({
el: '#pipeline-schedules-callout',
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js
new file mode 100644
index 00000000000..d65be6bc69e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/new/index.js
@@ -0,0 +1,3 @@
+import initForm from '../shared/init_form';
+
+document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 2d18fa2044b..2d18fa2044b 100644
--- a/app/assets/javascripts/pipeline_schedules/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
diff --git a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
index aa04a0ac47a..77508e62cef 100644
--- a/app/assets/javascripts/pipeline_schedules/components/pipeline_schedules_callout.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue
@@ -1,7 +1,7 @@
<script>
import Vue from 'vue';
import Cookies from 'js-cookie';
- import Translate from '../../vue_shared/translate';
+ import Translate from '../../../../../vue_shared/translate';
import illustrationSvg from '../icons/intro_illustration.svg';
Vue.use(Translate);
diff --git a/app/assets/javascripts/pipeline_schedules/components/target_branch_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js
index 0c3926d76b5..0c3926d76b5 100644
--- a/app/assets/javascripts/pipeline_schedules/components/target_branch_dropdown.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/target_branch_dropdown.js
diff --git a/app/assets/javascripts/pipeline_schedules/components/timezone_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
index 95ed9c7dc21..95ed9c7dc21 100644
--- a/app/assets/javascripts/pipeline_schedules/components/timezone_dropdown.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js
diff --git a/app/assets/javascripts/pipeline_schedules/icons/intro_illustration.svg b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg
index 26d1ff97b3e..26d1ff97b3e 100644
--- a/app/assets/javascripts/pipeline_schedules/icons/intro_illustration.svg
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg
diff --git a/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
index 0b1a81bae13..cfd30d6053f 100644
--- a/app/assets/javascripts/pipeline_schedules/pipeline_schedule_form_bundle.js
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js
@@ -1,10 +1,10 @@
import Vue from 'vue';
-import Translate from '../vue_shared/translate';
-import GlFieldErrors from '../gl_field_errors';
+import Translate from '../../../../vue_shared/translate';
+import GlFieldErrors from '../../../../gl_field_errors';
import intervalPatternInput from './components/interval_pattern_input.vue';
import TimezoneDropdown from './components/timezone_dropdown';
import TargetBranchDropdown from './components/target_branch_dropdown';
-import setupNativeFormVariableList from '../ci_variable_list/native_form_variable_list';
+import setupNativeFormVariableList from '../../../../ci_variable_list/native_form_variable_list';
Vue.use(Translate);
@@ -27,7 +27,7 @@ function initIntervalPatternInput() {
});
}
-document.addEventListener('DOMContentLoaded', () => {
+export default () => {
/* Most of the form is written in haml, but for fields with more complex behaviors,
* you should mount individual Vue components here. If at some point components need
* to share state, it may make sense to refactor the whole form to Vue */
@@ -46,4 +46,4 @@ document.addEventListener('DOMContentLoaded', () => {
container: $('.js-ci-variable-list-section'),
formField: 'schedule',
});
-});
+};
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js b/app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js
new file mode 100644
index 00000000000..d65be6bc69e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/update/index.js
@@ -0,0 +1,3 @@
+import initForm from '../shared/init_form';
+
+document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pages/projects/pipelines/builds/index.js b/app/assets/javascripts/pages/projects/pipelines/builds/index.js
index 060a78b427e..fbe9824c34b 100644
--- a/app/assets/javascripts/pages/projects/pipelines/builds/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/builds/index.js
@@ -1,16 +1,3 @@
-import Pipelines from '../../../../pipelines';
+import initPipelines from '../init_pipelines';
-export default () => {
- const { controllerAction } = document.querySelector('.js-pipeline-container').dataset;
- const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`;
-
- new Pipelines({ // eslint-disable-line no-new
- initTabs: true,
- pipelineStatusUrl,
- tabsOptions: {
- action: controllerAction,
- defaultAction: 'pipelines',
- parentEl: '.pipelines-tabs',
- },
- });
-};
+document.addEventListener('DOMContentLoaded', initPipelines);
diff --git a/app/assets/javascripts/pages/projects/pipelines/charts/index.js b/app/assets/javascripts/pages/projects/pipelines/charts/index.js
new file mode 100644
index 00000000000..bb92f4e1459
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipelines/charts/index.js
@@ -0,0 +1,56 @@
+import Chart from 'chart.js';
+
+const options = {
+ scaleOverlay: true,
+ responsive: true,
+ maintainAspectRatio: false,
+};
+
+const buildChart = (chartScope) => {
+ const data = {
+ labels: chartScope.labels,
+ datasets: [{
+ fillColor: '#707070',
+ strokeColor: '#707070',
+ pointColor: '#707070',
+ pointStrokeColor: '#EEE',
+ data: chartScope.totalValues,
+ },
+ {
+ fillColor: '#1aaa55',
+ strokeColor: '#1aaa55',
+ pointColor: '#1aaa55',
+ pointStrokeColor: '#fff',
+ data: chartScope.successValues,
+ },
+ ],
+ };
+ const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
+
+ new Chart(ctx).Line(data, options);
+};
+
+document.addEventListener('DOMContentLoaded', () => {
+ const chartTimesData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
+ const chartsData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
+ const data = {
+ labels: chartTimesData.labels,
+ datasets: [{
+ fillColor: 'rgba(220,220,220,0.5)',
+ strokeColor: 'rgba(220,220,220,1)',
+ barStrokeWidth: 1,
+ barValueSpacing: 1,
+ barDatasetSpacing: 1,
+ data: chartTimesData.values,
+ }],
+ };
+
+ if (window.innerWidth < 768) {
+ // Scale fonts if window width lower than 768px (iPad portrait)
+ options.scaleFontSize = 8;
+ }
+
+ new Chart($('#build_timesChart').get(0).getContext('2d')).Bar(data, options);
+
+ chartsData.forEach(scope => buildChart(scope));
+});
diff --git a/app/assets/javascripts/pages/projects/pipelines/failures/index.js b/app/assets/javascripts/pages/projects/pipelines/failures/index.js
new file mode 100644
index 00000000000..fbe9824c34b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipelines/failures/index.js
@@ -0,0 +1,3 @@
+import initPipelines from '../init_pipelines';
+
+document.addEventListener('DOMContentLoaded', initPipelines);
diff --git a/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
new file mode 100644
index 00000000000..94dfeb96e8c
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipelines/init_pipelines.js
@@ -0,0 +1,16 @@
+import Pipelines from '~/pipelines';
+
+export default () => {
+ const { controllerAction } = document.querySelector('.js-pipeline-container').dataset;
+ const pipelineStatusUrl = `${document.querySelector('.js-pipeline-tab-link a').getAttribute('href')}/status.json`;
+
+ new Pipelines({ // eslint-disable-line no-new
+ initTabs: true,
+ pipelineStatusUrl,
+ tabsOptions: {
+ action: controllerAction,
+ defaultAction: 'pipelines',
+ parentEl: '.pipelines-tabs',
+ },
+ });
+};
diff --git a/app/assets/javascripts/pages/projects/pipelines/new/index.js b/app/assets/javascripts/pages/projects/pipelines/new/index.js
index c54cc62bf05..da20bd995e9 100644
--- a/app/assets/javascripts/pages/projects/pipelines/new/index.js
+++ b/app/assets/javascripts/pages/projects/pipelines/new/index.js
@@ -1,5 +1,5 @@
-import NewBranchForm from '../../../../new_branch_form';
+import NewBranchForm from '~/new_branch_form';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new NewBranchForm($('.js-new-pipeline-form')); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/pipelines/show/index.js b/app/assets/javascripts/pages/projects/pipelines/show/index.js
new file mode 100644
index 00000000000..fbe9824c34b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/pipelines/show/index.js
@@ -0,0 +1,3 @@
+import initPipelines from '../init_pipelines';
+
+document.addEventListener('DOMContentLoaded', initPipelines);
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 863dac0d20e..d23ad9a92f4 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -50,7 +50,7 @@ export default class Project {
Project.projectSelectDropdown();
}
- static projectSelectDropdown () {
+ static projectSelectDropdown() {
projectSelect();
$('.project-item-select').on('click', e => Project.changeProject($(e.currentTarget).val()));
}
@@ -71,7 +71,7 @@ export default class Project {
selected = $dropdown.data('selected');
return $dropdown.glDropdown({
data(term, callback) {
- axios.get($dropdown.data('refs-url'), {
+ axios.get($dropdown.data('refsUrl'), {
params: {
ref: $dropdown.data('ref'),
search: term,
@@ -84,8 +84,8 @@ export default class Project {
filterable: true,
filterRemote: true,
filterByText: true,
- inputFieldName: $dropdown.data('input-field-name'),
- fieldName: $dropdown.data('field-name'),
+ inputFieldName: $dropdown.data('inputFieldName'),
+ fieldName: $dropdown.data('fieldName'),
renderRow: function(ref) {
var li = refListItem.cloneNode(false);
diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js
index f4643e7dba0..adbe744290a 100644
--- a/app/assets/javascripts/pages/projects/project_members/index.js
+++ b/app/assets/javascripts/pages/projects/project_members/index.js
@@ -3,10 +3,10 @@ import UsersSelect from '../../../users_select';
import groupsSelect from '../../../groups_select';
import Members from '../../../members';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
memberExpirationDate('.js-access-expiration-date-groups');
groupsSelect();
memberExpirationDate();
new Members(); // eslint-disable-line no-new
new UsersSelect(); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/releases/edit/index.js b/app/assets/javascripts/pages/projects/releases/edit/index.js
index 3d997cdfff0..0bf53a8de09 100644
--- a/app/assets/javascripts/pages/projects/releases/edit/index.js
+++ b/app/assets/javascripts/pages/projects/releases/edit/index.js
@@ -1,3 +1,3 @@
import initForm from '~/pages/projects/init_form';
-export default initForm($('.release-form'));
+document.addEventListener('DOMContentLoaded', () => initForm($('.release-form')));
diff --git a/app/assets/javascripts/pages/projects/services/edit/index.js b/app/assets/javascripts/pages/projects/services/edit/index.js
new file mode 100644
index 00000000000..ba4b271f09e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/services/edit/index.js
@@ -0,0 +1,13 @@
+import IntegrationSettingsForm from '~/integrations/integration_settings_form';
+import PrometheusMetrics from '~/prometheus_metrics/prometheus_metrics';
+
+document.addEventListener('DOMContentLoaded', () => {
+ const prometheusSettingsWrapper = document.querySelector('.js-prometheus-metrics-monitoring');
+ const integrationSettingsForm = new IntegrationSettingsForm('.js-integration-settings-form');
+ integrationSettingsForm.init();
+
+ if (prometheusSettingsWrapper) {
+ const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
+ prometheusMetrics.loadActiveMetrics();
+ }
+});
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index a563d0f9961..6c2a785c0af 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -2,7 +2,7 @@ import initSettingsPanels from '~/settings_panels';
import SecretValues from '~/behaviors/secret_values';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
-export default function () {
+document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels
initSettingsPanels();
@@ -22,4 +22,4 @@ export default function () {
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
-}
+});
diff --git a/app/assets/javascripts/pages/projects/settings/repository/show/index.js b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
index 83b5467fbc0..5a6f4138b10 100644
--- a/app/assets/javascripts/pages/projects/settings/repository/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/repository/show/index.js
@@ -1,3 +1,7 @@
import initSettingsPanels from '~/settings_panels';
+import initDeployKeys from '~/deploy_keys';
-export default initSettingsPanels;
+document.addEventListener('DOMContentLoaded', () => {
+ initDeployKeys();
+ initSettingsPanels();
+});
diff --git a/app/assets/javascripts/pages/projects/snippets/edit/index.js b/app/assets/javascripts/pages/projects/snippets/edit/index.js
index 9edb16dc73b..caf9ee9b398 100644
--- a/app/assets/javascripts/pages/projects/snippets/edit/index.js
+++ b/app/assets/javascripts/pages/projects/snippets/edit/index.js
@@ -1,3 +1,3 @@
import initForm from '~/pages/projects/init_form';
-export default initForm($('.snippet-form'));
+document.addEventListener('DOMContentLoaded', () => initForm($('.snippet-form')));
diff --git a/app/assets/javascripts/pages/projects/snippets/new/index.js b/app/assets/javascripts/pages/projects/snippets/new/index.js
index 9edb16dc73b..caf9ee9b398 100644
--- a/app/assets/javascripts/pages/projects/snippets/new/index.js
+++ b/app/assets/javascripts/pages/projects/snippets/new/index.js
@@ -1,3 +1,3 @@
import initForm from '~/pages/projects/init_form';
-export default initForm($('.snippet-form'));
+document.addEventListener('DOMContentLoaded', () => initForm($('.snippet-form')));
diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js
index a3cf75c385b..a134599cb04 100644
--- a/app/assets/javascripts/pages/projects/snippets/show/index.js
+++ b/app/assets/javascripts/pages/projects/snippets/show/index.js
@@ -3,9 +3,9 @@ import ZenMode from '~/zen_mode';
import LineHighlighter from '../../../../line_highlighter';
import BlobViewer from '../../../../blob/viewer';
-export default function () {
+document.addEventListener('DOMContentLoaded', () => {
new LineHighlighter(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
initNotes();
new ZenMode(); // eslint-disable-line no-new
-}
+});
diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js
index dacc2875c8c..191c98b36bb 100644
--- a/app/assets/javascripts/pages/projects/tags/new/index.js
+++ b/app/assets/javascripts/pages/projects/tags/new/index.js
@@ -2,8 +2,8 @@ import RefSelectDropdown from '../../../../ref_select_dropdown';
import ZenMode from '../../../../zen_mode';
import GLForm from '../../../../gl_form';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.tag-form'), true); // eslint-disable-line no-new
new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/projects/tree/show/index.js b/app/assets/javascripts/pages/projects/tree/show/index.js
index c4b3356e478..ed7d3f1747c 100644
--- a/app/assets/javascripts/pages/projects/tree/show/index.js
+++ b/app/assets/javascripts/pages/projects/tree/show/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import initBlob from '~/blob_edit/blob_bundle';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import TreeView from '../../../../tree';
import ShortcutsNavigation from '../../../../shortcuts_navigation';
@@ -6,7 +7,7 @@ import BlobViewer from '../../../../blob/viewer';
import NewCommitForm from '../../../../new_commit_form';
import { ajaxGet } from '../../../../lib/utils/common_utils';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new ShortcutsNavigation(); // eslint-disable-line no-new
new TreeView(); // eslint-disable-line no-new
new BlobViewer(); // eslint-disable-line no-new
@@ -14,7 +15,8 @@ export default () => {
$('#tree-slider').waitForImages(() =>
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
- const commitPipelineStatusEl = document.getElementById('commit-pipeline-status');
+ initBlob();
+ const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status');
const statusLink = document.querySelector('.commit-actions .ci-status-link');
if (statusLink != null) {
statusLink.remove();
@@ -33,5 +35,4 @@ export default () => {
},
});
}
-};
-
+});
diff --git a/app/assets/javascripts/pages/projects/wikis/index.js b/app/assets/javascripts/pages/projects/wikis/index.js
index eb14c7a0e78..b9f8707fd6e 100644
--- a/app/assets/javascripts/pages/projects/wikis/index.js
+++ b/app/assets/javascripts/pages/projects/wikis/index.js
@@ -3,9 +3,9 @@ import ShortcutsWiki from '../../../shortcuts_wiki';
import ZenMode from '../../../zen_mode';
import GLForm from '../../../gl_form';
-export default () => {
+document.addEventListener('DOMContentLoaded', () => {
new Wikis(); // eslint-disable-line no-new
new ShortcutsWiki(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.wiki-form'), true); // eslint-disable-line no-new
-};
+});
diff --git a/app/assets/javascripts/pages/search/init_filtered_search.js b/app/assets/javascripts/pages/search/init_filtered_search.js
index 250f9d992ab..de8d4168d71 100644
--- a/app/assets/javascripts/pages/search/init_filtered_search.js
+++ b/app/assets/javascripts/pages/search/init_filtered_search.js
@@ -1,7 +1,9 @@
+import FilteredSearchManager from '~/filtered_search/filtered_search_manager';
+
export default ({ page }) => {
- const filteredSearchEnabled = gl.FilteredSearchManager && document.querySelector('.filtered-search');
+ const filteredSearchEnabled = FilteredSearchManager && document.querySelector('.filtered-search');
if (filteredSearchEnabled) {
- const filteredSearchManager = new gl.FilteredSearchManager({ page });
+ const filteredSearchManager = new FilteredSearchManager({ page });
filteredSearchManager.setup();
}
};
diff --git a/app/assets/javascripts/pages/search/show/index.js b/app/assets/javascripts/pages/search/show/index.js
index 4264c5c9dbe..85aaaa2c9da 100644
--- a/app/assets/javascripts/pages/search/show/index.js
+++ b/app/assets/javascripts/pages/search/show/index.js
@@ -1,3 +1,3 @@
import Search from './search';
-export default () => new Search();
+document.addEventListener('DOMContentLoaded', () => new Search());
diff --git a/app/assets/javascripts/pages/search/show/search.js b/app/assets/javascripts/pages/search/show/search.js
index dc621bc87c0..cf44e291199 100644
--- a/app/assets/javascripts/pages/search/show/search.js
+++ b/app/assets/javascripts/pages/search/show/search.js
@@ -9,7 +9,7 @@ export default class Search {
this.searchInput = '.js-search-input';
this.searchClear = '.js-search-clear';
- this.groupId = $groupDropdown.data('group-id');
+ this.groupId = $groupDropdown.data('groupId');
this.eventListeners();
$groupDropdown.glDropdown({
@@ -36,7 +36,7 @@ export default class Search {
return obj.full_name;
},
toggleLabel(obj) {
- return `${($groupDropdown.data('default-label'))} ${obj.full_name}`;
+ return `${($groupDropdown.data('defaultLabel'))} ${obj.full_name}`;
},
clicked: () => Search.submitSearch(),
});
@@ -69,7 +69,7 @@ export default class Search {
return obj.name_with_namespace;
},
toggleLabel(obj) {
- return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`;
+ return `${($projectDropdown.data('defaultLabel'))} ${obj.name_with_namespace}`;
},
clicked: () => Search.submitSearch(),
});
diff --git a/app/assets/javascripts/pages/sessions/index.js b/app/assets/javascripts/pages/sessions/index.js
index 54f4e56359a..c2c069d1ca8 100644
--- a/app/assets/javascripts/pages/sessions/index.js
+++ b/app/assets/javascripts/pages/sessions/index.js
@@ -1,5 +1,3 @@
import initU2F from '../../shared/sessions/u2f';
-export default () => {
- initU2F();
-};
+document.addEventListener('DOMContentLoaded', initU2F);
diff --git a/app/assets/javascripts/pages/snippets/edit/index.js b/app/assets/javascripts/pages/snippets/edit/index.js
index 9c664b5f1ff..2ee38b64ca1 100644
--- a/app/assets/javascripts/pages/snippets/edit/index.js
+++ b/app/assets/javascripts/pages/snippets/edit/index.js
@@ -1,3 +1,3 @@
import form from '../form';
-export default form;
+document.addEventListener('DOMContentLoaded', form);
diff --git a/app/assets/javascripts/pages/snippets/new/index.js b/app/assets/javascripts/pages/snippets/new/index.js
index 9c664b5f1ff..2ee38b64ca1 100644
--- a/app/assets/javascripts/pages/snippets/new/index.js
+++ b/app/assets/javascripts/pages/snippets/new/index.js
@@ -1,3 +1,3 @@
import form from '../form';
-export default form;
+document.addEventListener('DOMContentLoaded', form);
diff --git a/app/assets/javascripts/pages/snippets/show/index.js b/app/assets/javascripts/pages/snippets/show/index.js
index 04c9562bfbb..f548b9fad65 100644
--- a/app/assets/javascripts/pages/snippets/show/index.js
+++ b/app/assets/javascripts/pages/snippets/show/index.js
@@ -1,12 +1,11 @@
-/* eslint-disable no-new */
import LineHighlighter from '../../../line_highlighter';
import BlobViewer from '../../../blob/viewer';
import ZenMode from '../../../zen_mode';
import initNotes from '../../../init_notes';
-export default () => {
- new LineHighlighter();
- new BlobViewer();
+document.addEventListener('DOMContentLoaded', () => {
+ new LineHighlighter(); // eslint-disable-line no-new
+ new BlobViewer(); // eslint-disable-line no-new
initNotes();
- new ZenMode();
-};
+ new ZenMode(); // eslint-disable-line no-new
+});
diff --git a/app/assets/javascripts/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 57306322aa4..57306322aa4 100644
--- a/app/assets/javascripts/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
diff --git a/app/assets/javascripts/users/index.js b/app/assets/javascripts/pages/users/index.js
index 9fd8452a2b6..899dcd42e37 100644
--- a/app/assets/javascripts/users/index.js
+++ b/app/assets/javascripts/pages/users/index.js
@@ -1,3 +1,4 @@
+import UserCallout from '~/user_callout';
import Cookies from 'js-cookie';
import UserTabs from './user_tabs';
@@ -22,4 +23,5 @@ document.addEventListener('DOMContentLoaded', () => {
const page = $('body').attr('data-page');
const action = page.split(':')[1];
initUserProfile(action);
+ new UserCallout(); // eslint-disable-line no-new
});
diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js
deleted file mode 100644
index f18f98b4e9a..00000000000
--- a/app/assets/javascripts/pages/users/show/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import UserCallout from '~/user_callout';
-
-export default () => new UserCallout();
diff --git a/app/assets/javascripts/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index e13b9839a20..c1217623467 100644
--- a/app/assets/javascripts/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -1,9 +1,9 @@
-import axios from '../lib/utils/axios_utils';
-import Activities from '../activities';
+import axios from '~/lib/utils/axios_utils';
+import Activities from '~/activities';
+import { localTimeAgo } from '~/lib/utils/datetime_utility';
+import { __ } from '~/locale';
+import flash from '~/flash';
import ActivityCalendar from './activity_calendar';
-import { localTimeAgo } from '../lib/utils/datetime_utility';
-import { __ } from '../locale';
-import flash from '../flash';
/**
* UserTabs
diff --git a/app/assets/javascripts/pipelines/components/async_button.vue b/app/assets/javascripts/pipelines/components/async_button.vue
index a5f22c4ec80..0cdffbde05b 100644
--- a/app/assets/javascripts/pipelines/components/async_button.vue
+++ b/app/assets/javascripts/pipelines/components/async_button.vue
@@ -31,10 +31,14 @@
type: String,
required: true,
},
- id: {
+ pipelineId: {
type: Number,
required: true,
},
+ type: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -46,17 +50,27 @@
return `btn ${this.cssClass}`;
},
},
+ created() {
+ // We're using eventHub to listen to the modal here instead of
+ // using props because it would would make the parent components
+ // much more complex to keep track of the loading state of each button
+ eventHub.$on('postAction', this.setLoading);
+ },
+ beforeDestroy() {
+ eventHub.$off('postAction', this.setLoading);
+ },
methods: {
onClick() {
- eventHub.$emit('actionConfirmationModal', {
- id: this.id,
- callback: this.makeRequest,
+ eventHub.$emit('openConfirmationModal', {
+ pipelineId: this.pipelineId,
+ endpoint: this.endpoint,
+ type: this.type,
});
},
- makeRequest() {
- this.isLoading = true;
-
- eventHub.$emit('postAction', this.endpoint);
+ setLoading(endpoint) {
+ if (endpoint === this.endpoint) {
+ this.isLoading = true;
+ }
},
},
};
diff --git a/app/assets/javascripts/pipelines/components/graph/graph_component.vue b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
index a1f58580318..ab84711d4a2 100644
--- a/app/assets/javascripts/pipelines/components/graph/graph_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/graph_component.vue
@@ -52,7 +52,7 @@
</script>
<template>
<div class="build-content middle-block js-pipeline-graph">
- <div class="pipeline-visualization pipeline-graph">
+ <div class="pipeline-visualization pipeline-graph pipeline-tab-content">
<div class="text-center">
<loading-icon
v-if="isLoading"
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 e027f08ff5c..7adcf4017b8 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -7,7 +7,6 @@
jobComponent,
dropdownJobComponent,
},
-
props: {
title: {
type: String,
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 62fe479fdf4..c9028952ddd 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -1,7 +1,8 @@
<script>
+ import modal from '~/vue_shared/components/modal.vue';
+ import { s__, sprintf } from '~/locale';
import pipelinesTableRowComponent from './pipelines_table_row.vue';
- import stopConfirmationModal from './stop_confirmation_modal.vue';
- import retryConfirmationModal from './retry_confirmation_modal.vue';
+ import eventHub from '../event_hub';
/**
* Pipelines Table Component.
@@ -11,8 +12,7 @@
export default {
components: {
pipelinesTableRowComponent,
- stopConfirmationModal,
- retryConfirmationModal,
+ modal,
},
props: {
pipelines: {
@@ -33,6 +33,52 @@
required: true,
},
},
+ data() {
+ return {
+ pipelineId: '',
+ endpoint: '',
+ type: '',
+ };
+ },
+ computed: {
+ modalTitle() {
+ return this.type === 'stop' ?
+ sprintf(s__('Pipeline|Stop pipeline #%{pipelineId}?'), {
+ pipelineId: `'${this.pipelineId}'`,
+ }, false) :
+ sprintf(s__('Pipeline|Retry pipeline #%{pipelineId}?'), {
+ pipelineId: `'${this.pipelineId}'`,
+ }, false);
+ },
+ modalText() {
+ return this.type === 'stop' ?
+ sprintf(s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'), {
+ pipelineId: `<strong>#${this.pipelineId}</strong>`,
+ }, false) :
+ sprintf(s__('Pipeline|You’re about to retry pipeline %{pipelineId}.'), {
+ pipelineId: `<strong>#${this.pipelineId}</strong>`,
+ }, false);
+ },
+ primaryButtonLabel() {
+ return this.type === 'stop' ? s__('Pipeline|Stop pipeline') : s__('Pipeline|Retry pipeline');
+ },
+ },
+ created() {
+ eventHub.$on('openConfirmationModal', this.setModalData);
+ },
+ beforeDestroy() {
+ eventHub.$off('openConfirmationModal', this.setModalData);
+ },
+ methods: {
+ setModalData(data) {
+ this.pipelineId = data.pipelineId;
+ this.endpoint = data.endpoint;
+ this.type = data.type;
+ },
+ onSubmit() {
+ eventHub.$emit('postAction', this.endpoint);
+ },
+ },
};
</script>
<template>
@@ -74,7 +120,20 @@
:auto-devops-help-path="autoDevopsHelpPath"
:view-type="viewType"
/>
- <stop-confirmation-modal />
- <retry-confirmation-modal />
+ <modal
+ id="confirmation-modal"
+ :title="modalTitle"
+ :text="modalText"
+ kind="danger"
+ :primary-button-label="primaryButtonLabel"
+ @submit="onSubmit"
+ >
+ <template
+ slot="body"
+ slot-scope="props"
+ >
+ <p v-html="props.text"></p>
+ </template>
+ </modal>
</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 0e3a10ed7f4..2ba59051773 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -223,7 +223,8 @@
<div class="table-section section-10 commit-link">
<div
class="table-mobile-header"
- role="rowheader">
+ role="rowheader"
+ >
Status
</div>
<div class="table-mobile-content">
@@ -305,9 +306,10 @@
css-class="js-pipelines-retry-button btn-default btn-retry"
title="Retry"
icon="repeat"
- :id="pipeline.id"
+ :pipeline-id="pipeline.id"
data-toggle="modal"
- data-target="#retry-confirmation-modal"
+ data-target="#confirmation-modal"
+ type="retry"
/>
<async-button-component
@@ -316,9 +318,10 @@
css-class="js-pipelines-cancel-button btn-remove"
title="Cancel"
icon="close"
- :id="pipeline.id"
+ :pipeline-id="pipeline.id"
data-toggle="modal"
- data-target="#stop-confirmation-modal"
+ data-target="#confirmation-modal"
+ type="stop"
/>
</div>
</div>
diff --git a/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue b/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue
deleted file mode 100644
index e2ac08d67bc..00000000000
--- a/app/assets/javascripts/pipelines/components/retry_confirmation_modal.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
- import modal from '~/vue_shared/components/modal.vue';
- import { s__, sprintf } from '~/locale';
- import eventHub from '../event_hub';
-
- export default {
- components: {
- modal,
- },
- data() {
- return {
- id: '',
- callback: () => {},
- };
- },
- computed: {
- title() {
- return sprintf(s__('Pipeline|Retry pipeline #%{id}?'), {
- id: `'${this.id}'`,
- }, false);
- },
- text() {
- return sprintf(s__('Pipeline|You’re about to retry pipeline %{id}.'), {
- id: `<strong>#${this.id}</strong>`,
- }, false);
- },
- primaryButtonLabel() {
- return s__('Pipeline|Retry pipeline');
- },
- },
- created() {
- eventHub.$on('actionConfirmationModal', this.updateModal);
- },
- beforeDestroy() {
- eventHub.$off('actionConfirmationModal', this.updateModal);
- },
- methods: {
- updateModal(action) {
- this.id = action.id;
- this.callback = action.callback;
- },
- onSubmit() {
- this.callback();
- },
- },
- };
-</script>
-
-<template>
- <modal
- id="retry-confirmation-modal"
- :title="title"
- :text="text"
- kind="danger"
- :primary-button-label="primaryButtonLabel"
- @submit="onSubmit"
- >
- <template
- slot="body"
- slot-scope="props"
- >
- <p v-html="props.text"></p>
- </template>
- </modal>
-</template>
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index 58806aa114a..ecf2b10486e 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -50,9 +50,7 @@
computed: {
dropdownClass() {
- return this.dropdownContent.length > 0 ?
- 'js-builds-dropdown-container' :
- 'js-builds-dropdown-loading';
+ return this.dropdownContent.length > 0 ? 'js-builds-dropdown-container' : 'js-builds-dropdown-loading';
},
triggerButtonClass() {
diff --git a/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue b/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue
deleted file mode 100644
index d737d567787..00000000000
--- a/app/assets/javascripts/pipelines/components/stop_confirmation_modal.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<script>
- import modal from '~/vue_shared/components/modal.vue';
- import { s__, sprintf } from '~/locale';
- import eventHub from '../event_hub';
-
- export default {
- components: {
- modal,
- },
- data() {
- return {
- id: '',
- callback: () => {},
- };
- },
- computed: {
- title() {
- return sprintf(s__('Pipeline|Stop pipeline #%{id}?'), {
- id: `'${this.id}'`,
- }, false);
- },
- text() {
- return sprintf(s__('Pipeline|You’re about to stop pipeline %{id}.'), {
- id: `<strong>#${this.id}</strong>`,
- }, false);
- },
- primaryButtonLabel() {
- return s__('Pipeline|Stop pipeline');
- },
- },
- created() {
- eventHub.$on('actionConfirmationModal', this.updateModal);
- },
- beforeDestroy() {
- eventHub.$off('actionConfirmationModal', this.updateModal);
- },
- methods: {
- updateModal(action) {
- this.id = action.id;
- this.callback = action.callback;
- },
- onSubmit() {
- this.callback();
- },
- },
- };
-</script>
-
-<template>
- <modal
- id="stop-confirmation-modal"
- :title="title"
- :text="text"
- kind="danger"
- :primary-button-label="primaryButtonLabel"
- @submit="onSubmit"
- >
- <template
- slot="body"
- slot-scope="props"
- >
- <p v-html="props.text"></p>
- </template>
- </modal>
-</template>
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index d88d280cb3f..705a60b3ba2 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -1,10 +1,14 @@
import Vue from 'vue';
-import Flash from '../flash';
-import PipelinesMediator from './pipeline_details_mediatior';
+import Flash from '~/flash';
+import Translate from '~/vue_shared/translate';
+import { __ } from '~/locale';
+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';
+Vue.use(Translate);
+
document.addEventListener('DOMContentLoaded', () => {
const dataset = document.querySelector('.js-pipeline-details-vue').dataset;
@@ -54,7 +58,7 @@ document.addEventListener('DOMContentLoaded', () => {
postAction(action) {
this.mediator.service.postAction(action.path)
.then(() => this.mediator.refreshPipeline())
- .catch(() => new Flash('An error occurred while making the request.'));
+ .catch(() => Flash(__('An error occurred while making the request.')));
},
},
render(createElement) {
diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
index 823ccd849f4..10f238fe73b 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_mediatior.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
@@ -1,6 +1,7 @@
import Visibility from 'visibilityjs';
import Flash from '../flash';
import Poll from '../lib/utils/poll';
+import { __ } from '../locale';
import PipelineStore from './stores/pipeline_store';
import PipelineService from './services/pipeline_service';
@@ -47,7 +48,7 @@ export default class pipelinesMediator {
errorCallback() {
this.state.isLoading = false;
- return new Flash('An error occurred while fetching the pipeline.');
+ Flash(__('An error occurred while fetching the pipeline.'));
}
refreshPipeline() {
diff --git a/app/assets/javascripts/pipelines/pipelines_charts.js b/app/assets/javascripts/pipelines/pipelines_charts.js
deleted file mode 100644
index 821aa7e229f..00000000000
--- a/app/assets/javascripts/pipelines/pipelines_charts.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import Chart from 'vendor/Chart';
-
-document.addEventListener('DOMContentLoaded', () => {
- const chartData = JSON.parse(document.getElementById('pipelinesChartsData').innerHTML);
- const buildChart = (chartScope) => {
- const data = {
- labels: chartScope.labels,
- datasets: [{
- fillColor: '#707070',
- strokeColor: '#707070',
- pointColor: '#707070',
- pointStrokeColor: '#EEE',
- data: chartScope.totalValues,
- },
- {
- fillColor: '#1aaa55',
- strokeColor: '#1aaa55',
- pointColor: '#1aaa55',
- pointStrokeColor: '#fff',
- data: chartScope.successValues,
- },
- ],
- };
- const ctx = $(`#${chartScope.scope}Chart`).get(0).getContext('2d');
- const options = {
- scaleOverlay: true,
- responsive: true,
- maintainAspectRatio: false,
- };
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8;
- }
- new Chart(ctx).Line(data, options);
- };
-
- chartData.forEach(scope => buildChart(scope));
-});
diff --git a/app/assets/javascripts/pipelines/pipelines_times.js b/app/assets/javascripts/pipelines/pipelines_times.js
deleted file mode 100644
index b5e7a0e53d9..00000000000
--- a/app/assets/javascripts/pipelines/pipelines_times.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import Chart from 'vendor/Chart';
-
-document.addEventListener('DOMContentLoaded', () => {
- const chartData = JSON.parse(document.getElementById('pipelinesTimesChartsData').innerHTML);
- const data = {
- labels: chartData.labels,
- datasets: [{
- fillColor: 'rgba(220,220,220,0.5)',
- strokeColor: 'rgba(220,220,220,1)',
- barStrokeWidth: 1,
- barValueSpacing: 1,
- barDatasetSpacing: 1,
- data: chartData.values,
- }],
- };
- const ctx = $('#build_timesChart').get(0).getContext('2d');
- const options = {
- scaleOverlay: true,
- responsive: true,
- maintainAspectRatio: false,
- };
- if (window.innerWidth < 768) {
- // Scale fonts if window width lower than 768px (iPad portrait)
- options.scaleFontSize = 8;
- }
- new Chart(ctx).Bar(data, options);
-});
diff --git a/app/assets/javascripts/profile/account/index.js b/app/assets/javascripts/profile/account/index.js
index a93bc935dd0..84049a1f0b7 100644
--- a/app/assets/javascripts/profile/account/index.js
+++ b/app/assets/javascripts/profile/account/index.js
@@ -1,29 +1,29 @@
import Vue from 'vue';
-
import Translate from '~/vue_shared/translate';
-
import deleteAccountModal from './components/delete_account_modal.vue';
-Vue.use(Translate);
+export default () => {
+ Vue.use(Translate);
-const deleteAccountButton = document.getElementById('delete-account-button');
-const deleteAccountModalEl = document.getElementById('delete-account-modal');
-// eslint-disable-next-line no-new
-new Vue({
- el: deleteAccountModalEl,
- components: {
- deleteAccountModal,
- },
- mounted() {
- deleteAccountButton.classList.remove('disabled');
- },
- render(createElement) {
- return createElement('delete-account-modal', {
- props: {
- actionUrl: deleteAccountModalEl.dataset.actionUrl,
- confirmWithPassword: !!deleteAccountModalEl.dataset.confirmWithPassword,
- username: deleteAccountModalEl.dataset.username,
- },
- });
- },
-});
+ const deleteAccountButton = document.getElementById('delete-account-button');
+ const deleteAccountModalEl = document.getElementById('delete-account-modal');
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: deleteAccountModalEl,
+ components: {
+ deleteAccountModal,
+ },
+ mounted() {
+ deleteAccountButton.classList.remove('disabled');
+ },
+ render(createElement) {
+ return createElement('delete-account-modal', {
+ props: {
+ actionUrl: deleteAccountModalEl.dataset.actionUrl,
+ confirmWithPassword: !!deleteAccountModalEl.dataset.confirmWithPassword,
+ username: deleteAccountModalEl.dataset.username,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index e116ee23601..930f0fb381e 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -33,9 +33,6 @@ import flash from '../flash';
$('input[name="user[multi_file]"]').on('change', this.setNewRepoCookie);
$('#user_notification_email').on('change', this.submitForm);
$('#user_notified_of_own_activity').on('change', this.submitForm);
- $('.update-username').on('ajax:before', this.beforeUpdateUsername);
- $('.update-username').on('ajax:complete', this.afterUpdateUsername);
- $('.update-notifications').on('ajax:success', this.onUpdateNotifs);
this.form.on('submit', this.onSubmitForm);
}
@@ -48,21 +45,6 @@ import flash from '../flash';
return this.saveForm();
}
- beforeUpdateUsername() {
- $('.loading-username', this).removeClass('hidden');
- }
-
- afterUpdateUsername() {
- $('.loading-username', this).addClass('hidden');
- $('button[type=submit]', this).enable();
- }
-
- onUpdateNotifs(e, data) {
- return data.saved ?
- flash(__('Notification settings saved'), 'notice') :
- flash(__('Failed to save new settings'));
- }
-
saveForm() {
const self = this;
const formData = new FormData(this.form[0]);
diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js
index 586d188350f..4fd639cce8e 100644
--- a/app/assets/javascripts/project_find_file.js
+++ b/app/assets/javascripts/project_find_file.js
@@ -73,7 +73,7 @@ export default class ProjectFindFile {
// find file
}
- // files pathes load
+ // files pathes load
load(url) {
axios.get(url)
.then(({ data }) => {
@@ -85,7 +85,7 @@ export default class ProjectFindFile {
.catch(() => flash(__('An error occurred while loading filenames')));
}
- // render result
+ // render result
renderList(filePaths, searchText) {
var blobItemUrl, filePath, html, i, j, len, matches, results;
this.element.find(".tree-table > tbody").empty();
diff --git a/app/assets/javascripts/project_select.js b/app/assets/javascripts/project_select.js
index 07a49d1506c..412aca7bfed 100644
--- a/app/assets/javascripts/project_select.js
+++ b/app/assets/javascripts/project_select.js
@@ -5,13 +5,13 @@ import ProjectSelectComboButton from './project_select_combo_button';
export default function projectSelect() {
$('.ajax-project-select').each(function(i, select) {
var placeholder;
- const simpleFilter = $(select).data('simple-filter') || false;
- this.groupId = $(select).data('group-id');
- this.includeGroups = $(select).data('include-groups');
- this.allProjects = $(select).data('all-projects') || false;
- this.orderBy = $(select).data('order-by') || 'id';
- this.withIssuesEnabled = $(select).data('with-issues-enabled');
- this.withMergeRequestsEnabled = $(select).data('with-merge-requests-enabled');
+ const simpleFilter = $(select).data('simpleFilter') || false;
+ this.groupId = $(select).data('groupId');
+ this.includeGroups = $(select).data('includeGroups');
+ this.allProjects = $(select).data('allProjects') || false;
+ this.orderBy = $(select).data('orderBy') || 'id';
+ this.withIssuesEnabled = $(select).data('withIssuesEnabled');
+ this.withMergeRequestsEnabled = $(select).data('withMergeRequestsEnabled');
placeholder = "Search for project";
if (this.includeGroups) {
diff --git a/app/assets/javascripts/projects/project_import_gitlab_project.js b/app/assets/javascripts/projects/project_import_gitlab_project.js
index cec6f0dd5a3..d2c7d77bb2d 100644
--- a/app/assets/javascripts/projects/project_import_gitlab_project.js
+++ b/app/assets/javascripts/projects/project_import_gitlab_project.js
@@ -1,14 +1,8 @@
import { getParameterValues } from '../lib/utils/url_utility';
-const bindEvents = () => {
+export default () => {
const path = getParameterValues('path')[0];
// get the path url and append it in the inputS
$('.js-path-name').val(path);
};
-
-document.addEventListener('DOMContentLoaded', bindEvents);
-
-export default {
- bindEvents,
-};
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index f5133111d04..8da37d14f0b 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -1,3 +1,5 @@
+import { addSelectOnFocusBehaviour } from '../lib/utils/common_utils';
+
let hasUserDefinedProjectPath = false;
const deriveProjectPathFromUrl = ($projectImportUrl) => {
@@ -36,6 +38,7 @@ const bindEvents = () => {
const $changeTemplateBtn = $('.change-template');
const $selectedIcon = $('.selected-icon svg');
const $templateProjectNameInput = $('#template-project-name #project_path');
+ const $pushNewProjectTipTrigger = $('.push-new-project-tip');
if ($newProjectForm.length !== 1) {
return;
@@ -55,6 +58,34 @@ const bindEvents = () => {
$('.btn_import_gitlab_project').attr('href', `${importHref}?namespace_id=${$('#project_namespace_id').val()}&path=${$projectPath.val()}`);
});
+ if ($pushNewProjectTipTrigger) {
+ $pushNewProjectTipTrigger
+ .removeAttr('rel')
+ .removeAttr('target')
+ .on('click', (e) => { e.preventDefault(); })
+ .popover({
+ title: $pushNewProjectTipTrigger.data('title'),
+ placement: 'auto bottom',
+ html: 'true',
+ content: $('.push-new-project-tip-template').html(),
+ })
+ .on('shown.bs.popover', () => {
+ $(document).on('click.popover touchstart.popover', (event) => {
+ if ($(event.target).closest('.popover').length === 0) {
+ $pushNewProjectTipTrigger.trigger('click');
+ }
+ });
+
+ const target = $(`#${$pushNewProjectTipTrigger.attr('aria-describedby')}`).find('.js-select-on-focus');
+ addSelectOnFocusBehaviour(target);
+
+ target.focus();
+ })
+ .on('hide.bs.popover', () => {
+ $(document).off('click.popover touchstart.popover');
+ });
+ }
+
function chooseTemplate() {
$('.template-option').hide();
$projectFieldsForm.addClass('selected');
diff --git a/app/assets/javascripts/prometheus_metrics/index.js b/app/assets/javascripts/prometheus_metrics/index.js
deleted file mode 100644
index a0c43c5abe1..00000000000
--- a/app/assets/javascripts/prometheus_metrics/index.js
+++ /dev/null
@@ -1,6 +0,0 @@
-import PrometheusMetrics from './prometheus_metrics';
-
-$(() => {
- const prometheusMetrics = new PrometheusMetrics('.js-prometheus-metrics-monitoring');
- prometheusMetrics.loadActiveMetrics();
-});
diff --git a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
index 59ad5b45855..e8126ac573d 100644
--- a/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
+++ b/app/assets/javascripts/prometheus_metrics/prometheus_metrics.js
@@ -19,7 +19,7 @@ export default class PrometheusMetrics {
this.$missingEnvVarMetricCount = this.$missingEnvVarPanel.find('.js-env-var-count');
this.$missingEnvVarMetricsList = this.$missingEnvVarPanel.find('.js-missing-var-metrics-list');
- this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('active-metrics');
+ this.activeMetricsEndpoint = this.$monitoredMetricsPanel.data('activeMetrics');
this.$panelToggle.on('click', e => this.handlePanelToggle(e));
}
diff --git a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js
index 38b1406a99f..40a873833e1 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_access_dropdown.js
@@ -9,8 +9,8 @@ export default class ProtectedBranchAccessDropdown {
$dropdown.glDropdown({
data,
selectable: true,
- inputId: $dropdown.data('input-id'),
- fieldName: $dropdown.data('field-name'),
+ inputId: $dropdown.data('inputId'),
+ fieldName: $dropdown.data('fieldName'),
toggleLabel(item, $el) {
if ($el.is('.is-active')) {
return item.text;
diff --git a/app/assets/javascripts/protected_branches/protected_branch_create.js b/app/assets/javascripts/protected_branches/protected_branch_create.js
index 2948baeab11..8fc87633e18 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_create.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_create.js
@@ -59,7 +59,7 @@ export default class ProtectedBranchCreate {
);
this.savePreviousSelection($allowedToMergeInput.val(), $allowedToPushInput.val());
- this.$form.find('input[type="submit"]').attr('disabled', completedForm);
+ this.$form.find('input[type="submit"]').prop('disabled', completedForm);
}
static getProtectedBranches(term, callback) {
diff --git a/app/assets/javascripts/protected_branches/protected_branch_edit.js b/app/assets/javascripts/protected_branches/protected_branch_edit.js
index b51b3e9a6ff..54560d08ad7 100644
--- a/app/assets/javascripts/protected_branches/protected_branch_edit.js
+++ b/app/assets/javascripts/protected_branches/protected_branch_edit.js
@@ -41,11 +41,11 @@ export default class ProtectedBranchEdit {
axios.patch(this.$wrap.data('url'), {
protected_branch: {
merge_access_levels_attributes: [{
- id: this.$allowedToMergeDropdown.data('access-level-id'),
+ id: this.$allowedToMergeDropdown.data('accessLevelId'),
access_level: $allowedToMergeInput.val(),
}],
push_access_levels_attributes: [{
- id: this.$allowedToPushDropdown.data('access-level-id'),
+ id: this.$allowedToPushDropdown.data('accessLevelId'),
access_level: $allowedToPushInput.val(),
}],
},
diff --git a/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js b/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js
index d4c9a91a74a..b803da798d5 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_access_dropdown.js
@@ -9,8 +9,8 @@ export default class ProtectedTagAccessDropdown {
this.options.$dropdown.glDropdown({
data: this.options.data,
selectable: true,
- inputId: this.options.$dropdown.data('input-id'),
- fieldName: this.options.$dropdown.data('field-name'),
+ inputId: this.options.$dropdown.data('inputId'),
+ fieldName: this.options.$dropdown.data('fieldName'),
toggleLabel(item, $el) {
if ($el.is('.is-active')) {
return item.text;
diff --git a/app/assets/javascripts/protected_tags/protected_tag_create.js b/app/assets/javascripts/protected_tags/protected_tag_create.js
index d1e4a75c17b..2f94ffe2507 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_create.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_create.js
@@ -39,7 +39,7 @@ export default class ProtectedTagCreate {
const $tagInput = this.$form.find('input[name="protected_tag[name]"]');
const $allowedToCreateInput = this.$form.find('#create_access_levels_attributes');
- this.$form.find('input[type="submit"]').attr('disabled', !($tagInput.val() && $allowedToCreateInput.length));
+ this.$form.find('input[type="submit"]').prop('disabled', !($tagInput.val() && $allowedToCreateInput.length));
}
static getProtectedTags(term, callback) {
diff --git a/app/assets/javascripts/protected_tags/protected_tag_edit.js b/app/assets/javascripts/protected_tags/protected_tag_edit.js
index 21a258cf93c..8687b2a4044 100644
--- a/app/assets/javascripts/protected_tags/protected_tag_edit.js
+++ b/app/assets/javascripts/protected_tags/protected_tag_edit.js
@@ -31,7 +31,7 @@ export default class ProtectedTagEdit {
axios.patch(this.$wrap.data('url'), {
protected_tag: {
create_access_levels_attributes: [{
- id: this.$allowedToCreateDropdownButton.data('access-level-id'),
+ id: this.$allowedToCreateDropdownButton.data('accessLevelId'),
access_level: $allowedToCreateInput.val(),
}],
},
diff --git a/app/assets/javascripts/ref_select_dropdown.js b/app/assets/javascripts/ref_select_dropdown.js
index 65e4101352c..56c25a35e6d 100644
--- a/app/assets/javascripts/ref_select_dropdown.js
+++ b/app/assets/javascripts/ref_select_dropdown.js
@@ -6,7 +6,7 @@ class RefSelectDropdown {
filterable: true,
filterByText: true,
remote: false,
- fieldName: $dropdownButton.data('field-name'),
+ fieldName: $dropdownButton.data('fieldName'),
filterInput: 'input[type="search"]',
selectable: true,
isSelectable(branch, $el) {
@@ -24,7 +24,7 @@ class RefSelectDropdown {
});
const $dropdownContainer = $dropdownButton.closest('.dropdown');
- const $fieldInput = $(`input[name="${$dropdownButton.data('field-name')}"]`, $dropdownContainer);
+ const $fieldInput = $(`input[name="${$dropdownButton.data('fieldName')}"]`, $dropdownContainer);
const $filterInput = $('input[type="search"]', $dropdownContainer);
$filterInput.on('keyup', (e) => {
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
index 5482c55f8bb..05a623ca6d9 100644
--- a/app/assets/javascripts/render_gfm.js
+++ b/app/assets/javascripts/render_gfm.js
@@ -1,6 +1,7 @@
import renderMath from './render_math';
import renderMermaid from './render_mermaid';
import syntaxHighlight from './syntax_highlight';
+
// Render Gitlab flavoured Markdown
//
// Delegates to syntax highlight and render math & mermaid diagrams.
diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js
index 73b6aafdd12..eabdb01b2a9 100644
--- a/app/assets/javascripts/render_math.js
+++ b/app/assets/javascripts/render_math.js
@@ -1,4 +1,5 @@
-/* global katex */
+import { __ } from './locale';
+import flash from './flash';
// Renders math using KaTeX in any element with the
// `js-render-math` class
@@ -8,15 +9,8 @@
// <code class="js-render-math"></div>
//
-import { __ } from './locale';
-import axios from './lib/utils/axios_utils';
-import flash from './flash';
-
-// Only load once
-let katexLoaded = false;
-
// Loop over all math elements and render math
-function renderWithKaTeX(elements) {
+function renderWithKaTeX(elements, katex) {
elements.each(function katexElementsLoop() {
const mathNode = $('<span></span>');
const $this = $(this);
@@ -34,30 +28,10 @@ function renderWithKaTeX(elements) {
export default function renderMath($els) {
if (!$els.length) return;
-
- if (katexLoaded) {
- renderWithKaTeX($els);
- } else {
- axios.get(gon.katex_css_url)
- .then(() => {
- const css = $('<link>', {
- rel: 'stylesheet',
- type: 'text/css',
- href: gon.katex_css_url,
- });
- css.appendTo('head');
- })
- .then(() => axios.get(gon.katex_js_url, {
- responseType: 'text',
- }))
- .then(({ data }) => {
- // Add katex js to our document
- $.globalEval(data);
- })
- .then(() => {
- katexLoaded = true;
- renderWithKaTeX($els); // Run KaTeX
- })
- .catch(() => flash(__('An error occurred while rendering KaTeX')));
- }
+ Promise.all([
+ import(/* webpackChunkName: 'katex' */ 'katex'),
+ import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.css'),
+ ]).then(([katex]) => {
+ renderWithKaTeX($els, katex);
+ }).catch(() => flash(__('An error occurred while rendering KaTeX')));
}
diff --git a/app/assets/javascripts/render_mermaid.js b/app/assets/javascripts/render_mermaid.js
index 31c7a772cf4..d4f18955bd2 100644
--- a/app/assets/javascripts/render_mermaid.js
+++ b/app/assets/javascripts/render_mermaid.js
@@ -30,6 +30,9 @@ export default function renderMermaid($els) {
$els.each((i, el) => {
const source = el.textContent;
+ // Remove any extra spans added by the backend syntax highlighting.
+ Object.assign(el, { textContent: source });
+
mermaid.init(undefined, el, (id) => {
const svg = document.getElementById(id);
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 01c3be5411f..8d3cc849f81 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -76,8 +76,8 @@ Sidebar.prototype.toggleTodo = function(e) {
$('.js-issuable-todo').disable().addClass('is-loading');
axios[ajaxType](url, {
- issuable_id: $this.data('issuable-id'),
- issuable_type: $this.data('issuable-type'),
+ issuable_id: $this.data('issuableId'),
+ issuable_type: $this.data('issuableType'),
}).then(({ data }) => {
this.todoUpdateDone(data);
}).catch(() => flash(`There was an error ${ajaxType === 'post' ? 'adding a' : 'deleting the'} todo.`));
@@ -96,18 +96,18 @@ Sidebar.prototype.todoUpdateDone = function(data) {
$el.removeClass('is-loading')
.enable()
- .attr('aria-label', $el.data(`${attrPrefix}-text`))
+ .attr('aria-label', $el.data(`${attrPrefix}Text`))
.attr('data-delete-path', deletePath)
- .attr('title', $el.data(`${attrPrefix}-text`));
+ .attr('title', $el.data(`${attrPrefix}Text`));
if ($el.hasClass('has-tooltip')) {
$el.tooltip('fixTitle');
}
- if ($el.data(`${attrPrefix}-icon`)) {
- $elText.html($el.data(`${attrPrefix}-icon`));
+ if ($el.data(`${attrPrefix}Icon`)) {
+ $elText.html($el.data(`${attrPrefix}Icon`));
} else {
- $elText.text($el.data(`${attrPrefix}-text`));
+ $elText.text($el.data(`${attrPrefix}Text`));
}
});
};
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
index 8f4a8704c3b..fdfa4f28aba 100644
--- a/app/assets/javascripts/search_autocomplete.js
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -1,5 +1,6 @@
/* eslint-disable no-return-assign, one-var, no-var, no-underscore-dangle, one-var-declaration-per-line, no-unused-vars, no-cond-assign, consistent-return, object-shorthand, prefer-arrow-callback, func-names, space-before-function-paren, prefer-template, quotes, class-methods-use-this, no-sequences, wrap-iife, no-lonely-if, no-else-return, no-param-reassign, vars-on-top, max-len */
import axios from './lib/utils/axios_utils';
+import DropdownUtils from './filtered_search/dropdown_utils';
import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from './lib/utils/common_utils';
/**
@@ -25,32 +26,32 @@ function setSearchOptions() {
if ($projectOptionsDataEl.length) {
gl.projectOptions = gl.projectOptions || {};
- var projectPath = $projectOptionsDataEl.data('project-path');
+ var projectPath = $projectOptionsDataEl.data('projectPath');
gl.projectOptions[projectPath] = {
name: $projectOptionsDataEl.data('name'),
- issuesPath: $projectOptionsDataEl.data('issues-path'),
- issuesDisabled: $projectOptionsDataEl.data('issues-disabled'),
- mrPath: $projectOptionsDataEl.data('mr-path'),
+ issuesPath: $projectOptionsDataEl.data('issuesPath'),
+ issuesDisabled: $projectOptionsDataEl.data('issuesDisabled'),
+ mrPath: $projectOptionsDataEl.data('mrPath'),
};
}
if ($groupOptionsDataEl.length) {
gl.groupOptions = gl.groupOptions || {};
- var groupPath = $groupOptionsDataEl.data('group-path');
+ var groupPath = $groupOptionsDataEl.data('groupPath');
gl.groupOptions[groupPath] = {
name: $groupOptionsDataEl.data('name'),
- issuesPath: $groupOptionsDataEl.data('issues-path'),
- mrPath: $groupOptionsDataEl.data('mr-path'),
+ issuesPath: $groupOptionsDataEl.data('issuesPath'),
+ mrPath: $groupOptionsDataEl.data('mrPath'),
};
}
if ($dashboardOptionsDataEl.length) {
gl.dashboardOptions = {
- issuesPath: $dashboardOptionsDataEl.data('issues-path'),
- mrPath: $dashboardOptionsDataEl.data('mr-path'),
+ issuesPath: $dashboardOptionsDataEl.data('issuesPath'),
+ mrPath: $dashboardOptionsDataEl.data('mrPath'),
};
}
}
@@ -61,9 +62,9 @@ export default class SearchAutocomplete {
this.bindEventContext();
this.wrap = wrap || $('.search');
this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
- this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path');
- this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || '');
- this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || '');
+ this.autocompletePath = autocompletePath || this.optsEl.data('autocompletePath');
+ this.projectId = projectId || (this.optsEl.data('autocompleteProjectId') || '');
+ this.projectRef = projectRef || (this.optsEl.data('autocompleteProjectRef') || '');
this.dropdown = this.wrap.find('.dropdown');
this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
this.dropdownContent = this.dropdown.find('.dropdown-content');
diff --git a/app/assets/javascripts/settings_panels.js b/app/assets/javascripts/settings_panels.js
index d34a21b37e1..d0e4f533d8a 100644
--- a/app/assets/javascripts/settings_panels.js
+++ b/app/assets/javascripts/settings_panels.js
@@ -42,7 +42,7 @@ export default function initSettingsPanels() {
if (location.hash) {
const $target = $(location.hash);
- if ($target.length && $target.hasClass('.settings')) {
+ if ($target.length && $target.hasClass('settings')) {
expandSection($target);
}
}
diff --git a/app/assets/javascripts/shortcuts_issuable.js b/app/assets/javascripts/shortcuts_issuable.js
index 689befc742e..14545824e74 100644
--- a/app/assets/javascripts/shortcuts_issuable.js
+++ b/app/assets/javascripts/shortcuts_issuable.js
@@ -9,13 +9,12 @@ export default class ShortcutsIssuable extends Shortcuts {
super();
this.$replyField = isMergeRequest ? $('.js-main-target-form #note_note') : $('.js-main-target-form .js-vue-comment-form');
- this.editBtn = document.querySelector('.js-issuable-edit');
Mousetrap.bind('a', () => ShortcutsIssuable.openSidebarDropdown('assignee'));
Mousetrap.bind('m', () => ShortcutsIssuable.openSidebarDropdown('milestone'));
Mousetrap.bind('l', () => ShortcutsIssuable.openSidebarDropdown('labels'));
Mousetrap.bind('r', this.replyWithSelectedText.bind(this));
- Mousetrap.bind('e', this.editIssue.bind(this));
+ Mousetrap.bind('e', ShortcutsIssuable.editIssue);
if (isMergeRequest) {
this.enabledHelp.push('.hidden-shortcut.merge_requests');
@@ -58,10 +57,10 @@ export default class ShortcutsIssuable extends Shortcuts {
return false;
}
- editIssue() {
+ static editIssue() {
// Need to click the element as on issues, editing is inline
// on merge request, editing is on a different page
- this.editBtn.click();
+ document.querySelector('.js-issuable-edit').click();
return false;
}
diff --git a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
index 3a344c89299..0686910fc7e 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -1,5 +1,5 @@
<script>
- import Flash from '../../../flash';
+ import Flash from '~/flash';
import editForm from './edit_form.vue';
import issuableMixin from '../../../vue_shared/mixins/issuable';
import Icon from '../../../vue_shared/components/icon.vue';
@@ -53,8 +53,7 @@
discussion_locked: locked,
})
.then(() => location.reload())
- .catch(() => Flash(this.__(`Something went wrong trying to
- change the locked state of this ${this.issuableDisplayName}`)));
+ .catch(() => Flash(this.__(`Something went wrong trying to change the locked state of this ${this.issuableDisplayName}`)));
},
},
};
@@ -75,7 +74,7 @@
{{ sprintf(__('Lock %{issuableDisplayName}'), { issuableDisplayName: issuableDisplayName }) }}
<button
v-if="isEditable"
- class="pull-right lock-edit btn btn-blank"
+ class="pull-right lock-edit"
type="button"
@click.prevent="toggleForm"
>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
index d32fe4abc7d..782e4ba4fad 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/sidebar_time_tracking.js
@@ -2,7 +2,7 @@ import _ from 'underscore';
import '~/smart_interval';
-import timeTracker from './time_tracker';
+import IssuableTimeTracker from './time_tracker.vue';
import Store from '../../stores/sidebar_store';
import Mediator from '../../sidebar_mediator';
@@ -16,7 +16,7 @@ export default {
};
},
components: {
- 'issuable-time-tracker': timeTracker,
+ IssuableTimeTracker,
},
methods: {
listenForQuickActions() {
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.js b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index 866178e2b23..230736a56b8 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.js
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -1,3 +1,4 @@
+<script>
import timeTrackingHelpState from './help_state';
import timeTrackingCollapsedState from './collapsed_state';
import timeTrackingSpentOnlyPane from './spent_only_pane';
@@ -8,7 +9,15 @@ import timeTrackingComparisonPane from './comparison_pane';
import eventHub from '../../event_hub';
export default {
- name: 'issuable-time-tracker',
+ name: 'IssuableTimeTracker',
+ components: {
+ 'time-tracking-collapsed-state': timeTrackingCollapsedState,
+ 'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane,
+ 'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane,
+ 'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane,
+ 'time-tracking-comparison-pane': timeTrackingComparisonPane,
+ 'time-tracking-help-state': timeTrackingHelpState,
+ },
props: {
time_estimate: {
type: Number,
@@ -38,14 +47,6 @@ export default {
showHelp: false,
};
},
- components: {
- 'time-tracking-collapsed-state': timeTrackingCollapsedState,
- 'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane,
- 'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane,
- 'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane,
- 'time-tracking-comparison-pane': timeTrackingComparisonPane,
- 'time-tracking-help-state': timeTrackingHelpState,
- },
computed: {
timeSpent() {
return this.time_spent;
@@ -81,6 +82,9 @@ export default {
return !!this.showHelp;
},
},
+ created() {
+ eventHub.$on('timeTracker:updateData', this.update);
+ },
methods: {
toggleHelpState(show) {
this.showHelp = show;
@@ -92,72 +96,73 @@ export default {
this.human_time_spent = data.human_time_spent;
},
},
- created() {
- eventHub.$on('timeTracker:updateData', this.update);
- },
- template: `
- <div
- class="time_tracker time-tracking-component-wrap"
- v-cloak
- >
- <time-tracking-collapsed-state
- :show-comparison-state="showComparisonState"
- :show-no-time-tracking-state="showNoTimeTrackingState"
- :show-help-state="showHelpState"
- :show-spent-only-state="showSpentOnlyState"
- :show-estimate-only-state="showEstimateOnlyState"
+};
+</script>
+
+<template>
+ <div
+ class="time_tracker time-tracking-component-wrap"
+ v-cloak
+ >
+ <time-tracking-collapsed-state
+ :show-comparison-state="showComparisonState"
+ :show-no-time-tracking-state="showNoTimeTrackingState"
+ :show-help-state="showHelpState"
+ :show-spent-only-state="showSpentOnlyState"
+ :show-estimate-only-state="showEstimateOnlyState"
+ :time-spent-human-readable="timeSpentHumanReadable"
+ :time-estimate-human-readable="timeEstimateHumanReadable"
+ />
+ <div class="title hide-collapsed">
+ {{ __('Time tracking') }}
+ <div
+ class="help-button pull-right"
+ v-if="!showHelpState"
+ @click="toggleHelpState(true)"
+ >
+ <i
+ class="fa fa-question-circle"
+ aria-hidden="true"
+ >
+ </i>
+ </div>
+ <div
+ class="close-help-button pull-right"
+ v-if="showHelpState"
+ @click="toggleHelpState(false)"
+ >
+ <i
+ class="fa fa-close"
+ aria-hidden="true"
+ >
+ </i>
+ </div>
+ </div>
+ <div class="time-tracking-content hide-collapsed">
+ <time-tracking-estimate-only-pane
+ v-if="showEstimateOnlyState"
+ :time-estimate-human-readable="timeEstimateHumanReadable"
+ />
+ <time-tracking-spent-only-pane
+ v-if="showSpentOnlyState"
+ :time-spent-human-readable="timeSpentHumanReadable"
+ />
+ <time-tracking-no-tracking-pane
+ v-if="showNoTimeTrackingState"
+ />
+ <time-tracking-comparison-pane
+ v-if="showComparisonState"
+ :time-estimate="timeEstimate"
+ :time-spent="timeSpent"
:time-spent-human-readable="timeSpentHumanReadable"
:time-estimate-human-readable="timeEstimateHumanReadable"
/>
- <div class="title hide-collapsed">
- {{ __('Time tracking') }}
- <div
- class="help-button pull-right"
- v-if="!showHelpState"
- @click="toggleHelpState(true)"
- >
- <i
- class="fa fa-question-circle"
- aria-hidden="true"
- />
- </div>
- <div
- class="close-help-button pull-right"
+ <transition name="help-state-toggle">
+ <time-tracking-help-state
v-if="showHelpState"
- @click="toggleHelpState(false)"
- >
- <i
- class="fa fa-close"
- aria-hidden="true"
- />
- </div>
- </div>
- <div class="time-tracking-content hide-collapsed">
- <time-tracking-estimate-only-pane
- v-if="showEstimateOnlyState"
- :time-estimate-human-readable="timeEstimateHumanReadable"
+ :root-path="rootPath"
/>
- <time-tracking-spent-only-pane
- v-if="showSpentOnlyState"
- :time-spent-human-readable="timeSpentHumanReadable"
- />
- <time-tracking-no-tracking-pane
- v-if="showNoTimeTrackingState"
- />
- <time-tracking-comparison-pane
- v-if="showComparisonState"
- :time-estimate="timeEstimate"
- :time-spent="timeSpent"
- :time-spent-human-readable="timeSpentHumanReadable"
- :time-estimate-human-readable="timeEstimateHumanReadable"
- />
- <transition name="help-state-toggle">
- <time-tracking-help-state
- v-if="showHelpState"
- :rootPath="rootPath"
- />
- </transition>
- </div>
+ </transition>
</div>
- `,
-};
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
index 977dd83a7ea..b10e2cc60ef 100644
--- a/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
+++ b/app/assets/javascripts/sidebar/lib/sidebar_move_issue.js
@@ -50,7 +50,7 @@ class SidebarMoveIssue {
const selectedProjectId = options.isMarking ? project.id : 0;
this.mediator.setMoveToProjectId(selectedProjectId);
- this.$confirmButton.attr('disabled', !isValidProjectId(selectedProjectId));
+ this.$confirmButton.prop('disabled', !isValidProjectId(selectedProjectId));
},
});
}
diff --git a/app/assets/javascripts/sidebar/mount_milestone_sidebar.js b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
new file mode 100644
index 00000000000..b15ad0e5586
--- /dev/null
+++ b/app/assets/javascripts/sidebar/mount_milestone_sidebar.js
@@ -0,0 +1,27 @@
+import Vue from 'vue';
+import timeTracker from './components/time_tracking/time_tracker.vue';
+
+export default class SidebarMilestone {
+ constructor() {
+ const el = document.getElementById('issuable-time-tracker');
+
+ if (!el) return;
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el,
+ components: {
+ timeTracker,
+ },
+ render: createElement => createElement('timeTracker', {
+ props: {
+ time_estimate: parseInt(el.dataset.timeEstimate, 10),
+ time_spent: parseInt(el.dataset.timeSpent, 10),
+ human_time_estimate: el.dataset.humanTimeEstimate,
+ human_time_spent: el.dataset.humanTimeSpent,
+ rootPath: '/',
+ },
+ }),
+ });
+ }
+}
diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js
index 48dd91bdf06..6142ce6c6a3 100644
--- a/app/assets/javascripts/single_file_diff.js
+++ b/app/assets/javascripts/single_file_diff.js
@@ -18,7 +18,7 @@ export default class SingleFileDiff {
this.toggleDiff = this.toggleDiff.bind(this);
this.content = $('.diff-content', this.file);
this.$toggleIcon = $('.diff-toggle-caret', this.file);
- this.diffForPath = this.content.find('[data-diff-for-path]').data('diff-for-path');
+ this.diffForPath = this.content.find('[data-diff-for-path]').data('diffForPath');
this.isOpen = !this.diffForPath;
if (this.diffForPath) {
this.collapsedContent = this.content;
@@ -88,6 +88,8 @@ export default class SingleFileDiff {
if (cb) cb();
})
- .catch(createFlash(__('An error occurred while retrieving diff')));
+ .catch(() => {
+ createFlash(__('An error occurred while retrieving diff'));
+ });
}
}
diff --git a/app/assets/javascripts/star.js b/app/assets/javascripts/star.js
index d5606e153f6..3deb629d5f2 100644
--- a/app/assets/javascripts/star.js
+++ b/app/assets/javascripts/star.js
@@ -1,17 +1,20 @@
import Flash from './flash';
import { __, s__ } from './locale';
import { spriteIcon } from './lib/utils/common_utils';
+import axios from './lib/utils/axios_utils';
export default class Star {
constructor() {
- $('.project-home-panel .toggle-star')
- .on('ajax:success', function handleSuccess(e, data) {
- const $this = $(this);
- const $starSpan = $this.find('span');
- const $startIcon = $this.find('svg');
+ $('.project-home-panel .toggle-star').on('click', function toggleStarClickCallback() {
+ const $this = $(this);
+ const $starSpan = $this.find('span');
+ const $startIcon = $this.find('svg');
- function toggleStar(isStarred) {
+ axios.post($this.data('endpoint'))
+ .then(({ data }) => {
+ const isStarred = $starSpan.hasClass('starred');
$this.parent().find('.star-count').text(data.star_count);
+
if (isStarred) {
$starSpan.removeClass('starred').text(s__('StarProject|Star'));
$startIcon.remove();
@@ -21,12 +24,8 @@ export default class Star {
$startIcon.remove();
$this.prepend(spriteIcon('star'));
}
- }
-
- toggleStar($starSpan.hasClass('starred'));
- })
- .on('ajax:error', () => {
- Flash('Star toggle failed. Try again later.', 'alert');
- });
+ })
+ .catch(() => Flash('Star toggle failed. Try again later.'));
+ });
}
}
diff --git a/app/assets/javascripts/subscription_select.js b/app/assets/javascripts/subscription_select.js
index 1ab4c2229ca..3ed064f87a9 100644
--- a/app/assets/javascripts/subscription_select.js
+++ b/app/assets/javascripts/subscription_select.js
@@ -1,6 +1,6 @@
export default function subscriptionSelect() {
$('.js-subscription-event').each((i, element) => {
- const fieldName = $(element).data('field-name');
+ const fieldName = $(element).data('fieldName');
return $(element).glDropdown({
selectable: true,
diff --git a/app/assets/javascripts/task_list.js b/app/assets/javascripts/task_list.js
index 129a551cbcd..8fa78b636f8 100644
--- a/app/assets/javascripts/task_list.js
+++ b/app/assets/javascripts/task_list.js
@@ -40,7 +40,7 @@ export default class TaskList {
[this.fieldName]: $target.val(),
};
- return axios.patch($target.data('update-url') || $('form.js-issuable-update').attr('action'), patchData)
+ return axios.patch($target.data('updateUrl') || $('form.js-issuable-update').attr('action'), patchData)
.then(({ data }) => this.onSuccess(data))
.catch(err => this.onError(err));
}
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js
index 4cc1c96b870..b5b64f44a11 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js
+++ b/app/assets/javascripts/templates/issuable_template_selector.js
@@ -6,9 +6,9 @@ import TemplateSelector from '../blob/template_selector';
export default class IssuableTemplateSelector extends TemplateSelector {
constructor(...args) {
super(...args);
- this.projectPath = this.dropdown.data('project-path');
- this.namespacePath = this.dropdown.data('namespace-path');
- this.issuableType = this.$dropdownContainer.data('issuable-type');
+ this.projectPath = this.dropdown.data('projectPath');
+ this.namespacePath = this.dropdown.data('namespacePath');
+ this.issuableType = this.$dropdownContainer.data('issuableType');
this.titleInput = $(`#${this.issuableType}_title`);
const initialQuery = {
diff --git a/app/assets/javascripts/ui_development_kit.js b/app/assets/javascripts/ui_development_kit.js
index f503076715c..78dda172ee6 100644
--- a/app/assets/javascripts/ui_development_kit.js
+++ b/app/assets/javascripts/ui_development_kit.js
@@ -1,6 +1,6 @@
import Api from './api';
-document.addEventListener('DOMContentLoaded', () => {
+export default () => {
$('#js-project-dropdown').glDropdown({
data: (term, callback) => {
Api.projects(term, {
@@ -19,4 +19,4 @@ document.addEventListener('DOMContentLoaded', () => {
id: data => data.id,
isSelected: data => (data.id === 2),
});
-});
+};
diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js
index a45b22f3084..a783122d500 100644
--- a/app/assets/javascripts/user_callout.js
+++ b/app/assets/javascripts/user_callout.js
@@ -22,7 +22,7 @@ export default class UserCallout {
const $currentTarget = $(e.currentTarget);
if (this.options.setCalloutPerProject) {
- Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('project-path') });
+ Cookies.set(this.cookieName, 'true', { expires: 365, path: this.userCalloutBody.data('projectPath') });
} else {
Cookies.set(this.cookieName, 'true', { expires: 365 });
}
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index eaed81cf79e..3385aba0279 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -34,23 +34,22 @@ function UsersSelect(currentUser, els, options = {}) {
var options = {};
var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, defaultNullUser, firstUser, issueURL, selectedId, selectedIdDefault, showAnyUser, showNullUser, showMenuAbove;
$dropdown = $(dropdown);
- options.projectId = $dropdown.data('project-id');
- options.groupId = $dropdown.data('group-id');
- options.showCurrentUser = $dropdown.data('current-user');
- options.todoFilter = $dropdown.data('todo-filter');
- options.todoStateFilter = $dropdown.data('todo-state-filter');
- options.perPage = $dropdown.data('per-page');
- showNullUser = $dropdown.data('null-user');
- defaultNullUser = $dropdown.data('null-user-default');
+ options.projectId = $dropdown.data('projectId');
+ options.groupId = $dropdown.data('groupId');
+ options.showCurrentUser = $dropdown.data('currentUser');
+ options.todoFilter = $dropdown.data('todoFilter');
+ options.todoStateFilter = $dropdown.data('todoStateFilter');
+ showNullUser = $dropdown.data('nullUser');
+ defaultNullUser = $dropdown.data('nullUserDefault');
showMenuAbove = $dropdown.data('showMenuAbove');
- showAnyUser = $dropdown.data('any-user');
- firstUser = $dropdown.data('first-user');
- options.authorId = $dropdown.data('author-id');
- defaultLabel = $dropdown.data('default-label');
+ showAnyUser = $dropdown.data('anyUser');
+ firstUser = $dropdown.data('firstUser');
+ options.authorId = $dropdown.data('authorId');
+ defaultLabel = $dropdown.data('defaultLabel');
issueURL = $dropdown.data('issueUpdate');
$selectbox = $dropdown.closest('.selectbox');
$block = $selectbox.closest('.block');
- abilityName = $dropdown.data('ability-name');
+ abilityName = $dropdown.data('abilityName');
$value = $block.find('.value');
$collapsedSidebar = $block.find('.sidebar-collapsed-user');
$loading = $block.find('.block-loading').fadeOut();
@@ -63,7 +62,7 @@ function UsersSelect(currentUser, els, options = {}) {
const assignYourself = function () {
const unassignedSelected = $dropdown.closest('.selectbox')
- .find(`input[name='${$dropdown.data('field-name')}'][value=0]`);
+ .find(`input[name='${$dropdown.data('fieldName')}'][value=0]`);
if (unassignedSelected) {
unassignedSelected.remove();
@@ -72,7 +71,7 @@ function UsersSelect(currentUser, els, options = {}) {
// Save current selected user to the DOM
const input = document.createElement('input');
input.type = 'hidden';
- input.name = $dropdown.data('field-name');
+ input.name = $dropdown.data('fieldName');
const currentUserInfo = $dropdown.data('currentUserInfo');
@@ -96,7 +95,7 @@ function UsersSelect(currentUser, els, options = {}) {
const getSelectedUserInputs = function() {
return $selectbox
- .find(`input[name="${$dropdown.data('field-name')}"]`);
+ .find(`input[name="${$dropdown.data('fieldName')}"]`);
};
const getSelected = function() {
@@ -106,14 +105,14 @@ function UsersSelect(currentUser, els, options = {}) {
};
const checkMaxSelect = function() {
- const maxSelect = $dropdown.data('max-select');
+ const maxSelect = $dropdown.data('maxSelect');
if (maxSelect) {
const selected = getSelected();
if (selected.length > maxSelect) {
const firstSelectedId = selected[0];
const firstSelected = $dropdown.closest('.selectbox')
- .find(`input[name='${$dropdown.data('field-name')}'][value=${firstSelectedId}]`);
+ .find(`input[name='${$dropdown.data('fieldName')}'][value=${firstSelectedId}]`);
firstSelected.remove();
emitSidebarEvent('sidebar.removeAssignee', {
@@ -158,7 +157,7 @@ function UsersSelect(currentUser, els, options = {}) {
const currentUserInfo = $dropdown.data('currentUserInfo');
$dropdown.find('.dropdown-toggle-text').text(getMultiSelectDropdownTitle(currentUserInfo)).removeClass('is-default');
} else {
- const $input = $(`input[name="${$dropdown.data('field-name')}"]`);
+ const $input = $(`input[name="${$dropdown.data('fieldName')}"]`);
$input.val(gon.current_user_id);
selectedId = $input.val();
$dropdown.find('.dropdown-toggle-text').text(gon.current_user_fullname).removeClass('is-default');
@@ -293,10 +292,10 @@ function UsersSelect(currentUser, els, options = {}) {
const selected = getSelected().filter(i => i !== 0);
if (selected.length > 0) {
- if ($dropdown.data('dropdown-header')) {
+ if ($dropdown.data('dropdownHeader')) {
showDivider += 1;
users.splice(showDivider, 0, {
- header: $dropdown.data('dropdown-header'),
+ header: $dropdown.data('dropdownHeader'),
});
}
@@ -327,7 +326,7 @@ function UsersSelect(currentUser, els, options = {}) {
fields: ['name', 'username']
},
selectable: true,
- fieldName: $dropdown.data('field-name'),
+ fieldName: $dropdown.data('fieldName'),
toggleLabel: function(selected, el, glDropdown) {
const inputValue = glDropdown.filterInput.val();
@@ -362,7 +361,7 @@ function UsersSelect(currentUser, els, options = {}) {
emitSidebarEvent('sidebar.saveAssignees');
}
- if (!$dropdown.data('always-show-selectbox')) {
+ if (!$dropdown.data('alwaysShowSelectbox')) {
$selectbox.hide();
// Recalculate where .value is because vue might have changed it
@@ -373,7 +372,7 @@ function UsersSelect(currentUser, els, options = {}) {
}
},
multiSelect: $dropdown.hasClass('js-multiselect'),
- inputMeta: $dropdown.data('input-meta'),
+ inputMeta: $dropdown.data('inputMeta'),
clicked: function(options) {
const { $el, e, isMarking } = options;
const user = options.selectedObj;
@@ -381,7 +380,7 @@ function UsersSelect(currentUser, els, options = {}) {
if ($dropdown.hasClass('js-multiselect')) {
const isActive = $el.hasClass('is-active');
const previouslySelected = $dropdown.closest('.selectbox')
- .find("input[name='" + ($dropdown.data('field-name')) + "'][value!=0]");
+ .find("input[name='" + ($dropdown.data('fieldName')) + "'][value!=0]");
// Enables support for limiting the number of users selected
// Automatically removes the first on the list if more users are selected
@@ -400,7 +399,7 @@ function UsersSelect(currentUser, els, options = {}) {
// Remove unassigned selection (if it was previously selected)
const unassignedSelected = $dropdown.closest('.selectbox')
- .find("input[name='" + ($dropdown.data('field-name')) + "'][value=0]");
+ .find("input[name='" + ($dropdown.data('fieldName')) + "'][value=0]");
if (unassignedSelected) {
unassignedSelected.remove();
@@ -408,7 +407,7 @@ function UsersSelect(currentUser, els, options = {}) {
} else {
if (previouslySelected.length === 0) {
// Select unassigned because there is no more selected users
- this.addInput($dropdown.data('field-name'), 0, {});
+ this.addInput($dropdown.data('fieldName'), 0, {});
}
// User unselected
@@ -440,7 +439,7 @@ function UsersSelect(currentUser, els, options = {}) {
return;
}
if ($el.closest('.add-issues-modal').length) {
- gl.issueBoards.ModalStore.store.filter[$dropdown.data('field-name')] = user.id;
+ gl.issueBoards.ModalStore.store.filter[$dropdown.data('fieldName')] = user.id;
} else if (handleClick) {
e.preventDefault();
handleClick(user, isMarking);
@@ -449,15 +448,15 @@ function UsersSelect(currentUser, els, options = {}) {
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
} else if (!$dropdown.hasClass('js-multiselect')) {
- selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('field-name')) + "']").val();
+ selected = $dropdown.closest('.selectbox').find("input[name='" + ($dropdown.data('fieldName')) + "']").val();
return assignTo(selected);
}
// Automatically close dropdown after assignee is selected
// since CE has no multiple assignees
// EE does not have a max-select
- if ($dropdown.data('max-select') &&
- getSelected().length === $dropdown.data('max-select')) {
+ if ($dropdown.data('maxSelect') &&
+ getSelected().length === $dropdown.data('maxSelect')) {
// Close the dropdown
$dropdown.dropdown('toggle');
}
@@ -469,7 +468,7 @@ function UsersSelect(currentUser, els, options = {}) {
const $el = $(e.currentTarget);
const selected = getSelected();
if ($dropdown.hasClass('js-issue-board-sidebar') && selected.length === 0) {
- this.addInput($dropdown.data('field-name'), 0, {});
+ this.addInput($dropdown.data('fieldName'), 0, {});
}
$el.find('.is-active').removeClass('is-active');
@@ -485,7 +484,7 @@ function UsersSelect(currentUser, els, options = {}) {
highlightSelected(selectedId);
}
},
- updateLabel: $dropdown.data('dropdown-title'),
+ updateLabel: $dropdown.data('dropdownTitle'),
renderRow: function(user) {
var avatar, img, listClosingTags, listWithName, listWithUserName, username;
username = user.username ? "@" + user.username : "";
@@ -533,15 +532,15 @@ function UsersSelect(currentUser, els, options = {}) {
var firstUser, showAnyUser, showEmailUser, showNullUser;
var options = {};
options.skipLdap = $(select).hasClass('skip_ldap');
- options.projectId = $(select).data('project-id');
- options.groupId = $(select).data('group-id');
- options.showCurrentUser = $(select).data('current-user');
- options.authorId = $(select).data('author-id');
- options.skipUsers = $(select).data('skip-users');
- showNullUser = $(select).data('null-user');
- showAnyUser = $(select).data('any-user');
- showEmailUser = $(select).data('email-user');
- firstUser = $(select).data('first-user');
+ options.projectId = $(select).data('projectId');
+ options.groupId = $(select).data('groupId');
+ options.showCurrentUser = $(select).data('currentUser');
+ options.authorId = $(select).data('authorId');
+ options.skipUsers = $(select).data('skipUsers');
+ showNullUser = $(select).data('nullUser');
+ showAnyUser = $(select).data('anyUser');
+ showEmailUser = $(select).data('emailUser');
+ firstUser = $(select).data('firstUser');
return $(select).select2({
placeholder: "Search for a user",
multiple: $(select).hasClass('multiselect'),
@@ -669,7 +668,6 @@ UsersSelect.prototype.users = function(query, options, callback) {
const url = this.buildUrl(this.usersPath);
const params = {
search: query,
- per_page: options.perPage || 20,
active: true,
project_id: options.projectId || null,
group_id: options.groupId || null,
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 109a302a172..54a98abf860 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,8 +1,8 @@
<script>
/* eslint-disable vue/require-default-prop */
- 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 pipelineStage from '~/pipelines/components/stage.vue';
+ import ciIcon from '~/vue_shared/components/ci_icon.vue';
+ import icon from '~/vue_shared/components/icon.vue';
export default {
name: 'MRWidgetPipeline',
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
index 40c3cb500bb..ebaf2b972eb 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue
@@ -44,7 +44,10 @@
type="button"
class="btn btn-xs btn-default"
>
- <loading-icon v-if="isRefreshing" />
+ <loading-icon
+ v-if="isRefreshing"
+ :inline="true"
+ />
{{ s__("mrWidget|Refresh") }}
</button>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
deleted file mode 100644
index 7733fb74afe..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.js
+++ /dev/null
@@ -1,43 +0,0 @@
-import statusIcon from '../mr_widget_status_icon.vue';
-import tooltip from '../../../vue_shared/directives/tooltip';
-import mrWidgetMergeHelp from '../../components/mr_widget_merge_help.vue';
-
-export default {
- name: 'MRWidgetMissingBranch',
- props: {
- mr: { type: Object, required: true },
- },
- directives: {
- tooltip,
- },
- components: {
- 'mr-widget-merge-help': mrWidgetMergeHelp,
- statusIcon,
- },
- computed: {
- missingBranchName() {
- return this.mr.sourceBranchRemoved ? 'source' : 'target';
- },
- message() {
- return `If the ${this.missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line`;
- },
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="warning" :show-disabled-button="true" />
- <div class="media-body space-children">
- <span class="bold js-branch-text">
- <span class="capitalize">
- {{missingBranchName}}
- </span> branch does not exist.
- Please restore it or use a different {{missingBranchName}} branch
- <i
- v-tooltip
- class="fa fa-question-circle"
- :title="message"
- :aria-label="message"></i>
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
new file mode 100644
index 00000000000..718c0e4b3c6
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
@@ -0,0 +1,62 @@
+<script>
+ import { sprintf, s__ } from '~/locale';
+ import tooltip from '~/vue_shared/directives/tooltip';
+ import statusIcon from '../mr_widget_status_icon.vue';
+ import mrWidgetMergeHelp from '../../components/mr_widget_merge_help.vue';
+
+ export default {
+ name: 'MRWidgetMissingBranch',
+ directives: {
+ tooltip,
+ },
+ components: {
+ mrWidgetMergeHelp,
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ },
+ },
+ computed: {
+ missingBranchName() {
+ return this.mr.sourceBranchRemoved ? 'source' : 'target';
+ },
+ missingBranchNameMessage() {
+ return sprintf(s__('mrWidget| Please restore it or use a different %{missingBranchName} branch'), {
+ missingBranchName: this.missingBranchName,
+ });
+ },
+ message() {
+ return sprintf(s__('mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line'), {
+ missingBranchName: this.missingBranchName,
+ });
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon
+ status="warning"
+ :show-disabled-button="true"
+ />
+
+ <div class="media-body space-children">
+ <span class="bold js-branch-text">
+ <span class="capitalize">
+ {{ missingBranchName }}
+ </span> {{ s__("mrWidget|branch does not exist.") }}
+ {{ missingBranchNameMessage }}
+ <i
+ v-tooltip
+ class="fa fa-question-circle"
+ :title="message"
+ :aria-label="message"
+ >
+ </i>
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
deleted file mode 100644
index cea3d97fa88..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import statusIcon from '../mr_widget_status_icon.vue';
-
-export default {
- name: 'MRWidgetNotAllowed',
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="success" :show-disabled-button="true" />
- <div class="media-body space-children">
- <span class="bold">
- Ready to be merged automatically.
- Ask someone with write access to this repository to merge this request
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
new file mode 100644
index 00000000000..e4af50b09f8
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
@@ -0,0 +1,25 @@
+<script>
+ import StatusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetNotAllowed',
+ components: {
+ StatusIcon,
+ },
+ };
+</script>
+
+<template>
+ <div class="mr-widget-body media">
+ <status-icon
+ status="success"
+ :show-disabled-button="true"
+ />
+ <div class="media-body space-children">
+ <span class="bold">
+ {{ s__(`mrWidget|Ready to be merged automatically.
+Ask someone with write access to this repository to merge this request`) }}
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
deleted file mode 100644
index e66ce071ab4..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import statusIcon from '../mr_widget_status_icon.vue';
-
-export default {
- name: 'MRWidgetPipelineBlocked',
- components: {
- statusIcon,
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="warning" :show-disabled-button="true" />
- <div class="media-body space-children">
- <span class="bold">
- Pipeline blocked. The pipeline for this merge request requires a manual action to proceed
- </span>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
new file mode 100644
index 00000000000..6d7cc03f7ad
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
@@ -0,0 +1,24 @@
+<script>
+ import StatusIcon from '../mr_widget_status_icon.vue';
+
+ export default {
+ name: 'MRWidgetPipelineBlocked',
+ components: {
+ StatusIcon,
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon
+ status="warning"
+ :show-disabled-button="true"
+ />
+ <div class="media-body space-children">
+ <span class="bold">
+ {{ s__(`mrWidget|Pipeline blocked.
+The pipeline for this merge request requires a manual action to proceed`) }}
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
index 7ba6c29006a..162f048aac7 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js
@@ -227,7 +227,8 @@ export default {
@click="handleMergeButtonClick()"
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
- type="button">
+ type="button"
+ class="qa-merge-button">
<i
v-if="isMakingRequest"
class="fa fa-spinner fa-spin"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
index 2968af0d5cb..143fd328d88 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue
@@ -107,10 +107,11 @@
<template v-if="!mr.rebaseInProgress && mr.canPushToSourceBranch && !isMakingRequest">
<div
class="accept-merge-holder clearfix
-js-toggle-container accept-action media space-children">
+js-toggle-container accept-action media space-children"
+ >
<button
type="button"
- class="btn btn-sm btn-reopen btn-success"
+ class="btn btn-sm btn-reopen btn-success qa-mr-rebase-button"
:disabled="isMakingRequest"
@click="rebase"
>
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index 7ca15537719..edb3baa39e4 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -24,12 +24,12 @@ export { default as WipState } from './components/states/mr_widget_wip';
export { default as ArchivedState } from './components/states/mr_widget_archived.vue';
export { default as ConflictsState } from './components/states/mr_widget_conflicts.vue';
export { default as NothingToMergeState } from './components/states/mr_widget_nothing_to_merge';
-export { default as MissingBranchState } from './components/states/mr_widget_missing_branch';
-export { default as NotAllowedState } from './components/states/mr_widget_not_allowed';
+export { default as MissingBranchState } from './components/states/mr_widget_missing_branch.vue';
+export { default as NotAllowedState } from './components/states/mr_widget_not_allowed.vue';
export { default as ReadyToMergeState } from './components/states/mr_widget_ready_to_merge';
export { default as SHAMismatchState } from './components/states/mr_widget_sha_mismatch';
export { default as UnresolvedDiscussionsState } from './components/states/mr_widget_unresolved_discussions';
-export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked';
+export { default as PipelineBlockedState } from './components/states/mr_widget_pipeline_blocked.vue';
export { default as PipelineFailedState } from './components/states/mr_widget_pipeline_failed';
export { default as MergeWhenPipelineSucceedsState } from './components/states/mr_widget_merge_when_pipeline_succeeds.vue';
export { default as RebaseState } from './components/states/mr_widget_rebase.vue';
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
index edf67fcd0a7..797f0f6ec0f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.js
@@ -96,9 +96,7 @@ export default {
cb.call(null, data);
}
})
- .catch(() => {
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
- });
+ .catch(() => new Flash('Something went wrong. Please try again.'));
},
initPolling() {
this.pollingInterval = new SmartInterval({
@@ -146,12 +144,11 @@ export default {
Project.initRefSwitcher();
}
})
- .catch(() => {
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
- });
+ .catch(() => new Flash('Something went wrong. Please try again.'));
},
handleNotification(data) {
if (data.ci_status === this.mr.ciStatus) return;
+ if (!data.pipeline) return;
const label = data.pipeline.details.status.label;
const title = `Pipeline ${label}`;
diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
index ed004b3bb08..9a750ce42bd 100644
--- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
+++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js
@@ -4,7 +4,6 @@ import { stateKey } from './state_maps';
import { formatDate } from '../../lib/utils/datetime_utility';
export default class MergeRequestStore {
-
constructor(data) {
this.sha = data.diff_head_sha;
this.gitlabLogo = data.gitlabLogo;
@@ -169,5 +168,4 @@ export default class MergeRequestStore {
return timeagoInstance.format(date);
}
-
}
diff --git a/app/assets/javascripts/vue_shared/components/confirmation_input.vue b/app/assets/javascripts/vue_shared/components/confirmation_input.vue
deleted file mode 100644
index 1aa03ea6317..00000000000
--- a/app/assets/javascripts/vue_shared/components/confirmation_input.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<script>
- import _ from 'underscore';
- import { __, sprintf } from '~/locale';
-
- export default {
- props: {
- inputId: {
- type: String,
- required: true,
- },
- confirmationKey: {
- type: String,
- required: true,
- },
- confirmationValue: {
- type: String,
- required: true,
- },
- shouldEscapeConfirmationValue: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
- computed: {
- inputLabel() {
- let value = this.confirmationValue;
- if (this.shouldEscapeConfirmationValue) {
- value = _.escape(value);
- }
-
- return sprintf(
- __('Type %{value} to confirm:'),
- { value: `<code>${value}</code>` },
- false,
- );
- },
- },
- methods: {
- hasCorrectValue() {
- return this.$refs.enteredValue.value === this.confirmationValue;
- },
- },
- };
-</script>
-
-<template>
- <div>
- <label
- v-html="inputLabel"
- :for="inputId"
- >
- </label>
- <input
- :id="inputId"
- :name="confirmationKey"
- type="text"
- ref="enteredValue"
- class="form-control"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
new file mode 100644
index 00000000000..67c9181c7b1
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -0,0 +1,106 @@
+<script>
+ const buttonVariants = [
+ 'danger',
+ 'primary',
+ 'success',
+ 'warning',
+ ];
+
+ export default {
+ name: 'GlModal',
+
+ props: {
+ id: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ headerTitleText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ footerPrimaryButtonVariant: {
+ type: String,
+ required: false,
+ default: 'primary',
+ validator: value => buttonVariants.indexOf(value) !== -1,
+ },
+ footerPrimaryButtonText: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+
+ methods: {
+ emitCancel(event) {
+ this.$emit('cancel', event);
+ },
+ emitSubmit(event) {
+ this.$emit('submit', event);
+ },
+ },
+ };
+</script>
+
+<template>
+ <div
+ :id="id"
+ class="modal fade"
+ tabindex="-1"
+ role="dialog"
+ >
+ <div
+ class="modal-dialog"
+ role="document"
+ >
+ <div class="modal-content">
+ <div class="modal-header">
+ <slot name="header">
+ <button
+ type="button"
+ class="close"
+ data-dismiss="modal"
+ :aria-label="s__('Modal|Close')"
+ @click="emitCancel($event)"
+ >
+ <span aria-hidden="true">&times;</span>
+ </button>
+ <h4 class="modal-title">
+ <slot name="title">
+ {{ headerTitleText }}
+ </slot>
+ </h4>
+ </slot>
+ </div>
+
+ <div class="modal-body">
+ <slot></slot>
+ </div>
+
+ <div class="modal-footer">
+ <slot name="footer">
+ <button
+ type="button"
+ class="btn"
+ data-dismiss="modal"
+ @click="emitCancel($event)"
+ >
+ {{ s__('Modal|Cancel') }}
+ </button>
+ <button
+ type="button"
+ class="btn"
+ :class="`btn-${footerPrimaryButtonVariant}`"
+ data-dismiss="modal"
+ @click="emitSubmit($event)"
+ >
+ {{ footerPrimaryButtonText }}
+ </button>
+ </slot>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 1f72dea1b33..a0cd0cbd200 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -6,12 +6,12 @@
import userAvatarImage from './user_avatar/user_avatar_image.vue';
/**
- * Renders header component for job and pipeline page based on UI mockups
- *
- * Used in:
- * - job show page
- * - pipeline show page
- */
+ * Renders header component for job and pipeline page based on UI mockups
+ *
+ * Used in:
+ * - job show page
+ * - pipeline show page
+ */
export default {
components: {
ciIconBadge,
@@ -118,7 +118,8 @@
<section
class="header-action-buttons"
- v-if="actions.length">
+ v-if="actions.length"
+ >
<template
v-for="(action, i) in actions"
>
diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue
index ff8c0f7c1d2..e832d94d32f 100644
--- a/app/assets/javascripts/vue_shared/components/loading_button.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_button.vue
@@ -1,6 +1,5 @@
<script>
/* eslint-disable vue/require-default-prop */
-
/* This is a re-usable vue component for rendering a button
that will probably be sending off ajax requests and need
to show the loading status by setting the `loading` option.
@@ -40,7 +39,7 @@
required: false,
},
containerClass: {
- type: String,
+ type: [String, Array, Object],
required: false,
default: 'btn btn-align-content',
},
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 1371dca0c35..d2e968a8419 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -64,7 +64,7 @@
return new GLForm($(this.$refs['gl-form']), this.enableAutocomplete);
},
beforeDestroy() {
- const glForm = $(this.$refs['gl-form']).data('gl-form');
+ const glForm = $(this.$refs['gl-form']).data('glForm');
if (glForm) {
glForm.destroy();
}
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index f65eab11a27..177d2cfc8da 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -65,7 +65,8 @@
</li>
<li
class="md-header-tab"
- :class="{ active: previewMarkdown }">
+ :class="{ active: previewMarkdown }"
+ >
<a
class="js-preview-link"
href="#md-preview-holder"
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue
index c44c606a8b2..22fc5757447 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue
@@ -13,6 +13,12 @@
props: {
/**
This function will take the information given by the pagination component
+
+ Here is an example `change` method:
+
+ change(pagenum) {
+ gl.utils.visitUrl(`?page=${pagenum}`);
+ },
*/
change: {
type: Function,
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 887879ab715..2fccfa4011c 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -21,7 +21,7 @@
@import "framework/flash";
@import "framework/forms";
@import "framework/gfm";
-@import "framework/gitlab-theme";
+@import "framework/gitlab_theme";
@import "framework/header";
@import "framework/highlight";
@import "framework/issue_box";
@@ -35,10 +35,10 @@
@import "framework/pagination";
@import "framework/panels";
@import "framework/popup";
-@import "framework/secondary-navigation-elements";
+@import "framework/secondary_navigation_elements";
@import "framework/selects";
@import "framework/sidebar";
-@import "framework/contextual-sidebar";
+@import "framework/contextual_sidebar";
@import "framework/tables";
@import "framework/notes";
@import "framework/tabs";
@@ -49,16 +49,16 @@
@import "framework/zen";
@import "framework/blank";
@import "framework/wells";
-@import "framework/page-header";
+@import "framework/page_header";
@import "framework/awards";
@import "framework/images";
-@import "framework/broadcast-messages";
+@import "framework/broadcast_messages";
@import "framework/emojis";
-@import "framework/emoji-sprites";
+@import "framework/emoji_sprites";
@import "framework/icons";
@import "framework/snippets";
@import "framework/memory_graph";
@import "framework/responsive_tables";
-@import "framework/stacked-progress-bar";
+@import "framework/stacked_progress_bar";
@import "framework/ci_variable_list";
@import "framework/feature_highlight";
diff --git a/app/assets/stylesheets/framework/broadcast-messages.scss b/app/assets/stylesheets/framework/broadcast_messages.scss
index 9b54fb94cdc..9b54fb94cdc 100644
--- a/app/assets/stylesheets/framework/broadcast-messages.scss
+++ b/app/assets/stylesheets/framework/broadcast_messages.scss
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index c4b046a6d68..6b89387ab5f 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -444,6 +444,19 @@
}
}
+.btn-missing {
+ color: $notes-light-color;
+ border: 1px dashed $border-gray-normal-dashed;
+ border-radius: $border-radius-default;
+
+ &:hover,
+ &:active,
+ &:focus {
+ color: $notes-light-color;
+ background-color: $white-normal;
+ }
+}
+
.btn-svg svg {
@include btn-svg;
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 73524d5cf60..ae517c41cb2 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -449,9 +449,11 @@ img.emoji {
.prepend-top-10 { margin-top: 10px; }
.prepend-top-15 { margin-top: 15px; }
.prepend-top-default { margin-top: $gl-padding !important; }
+.prepend-top-16 { margin-top: 16px; }
.prepend-top-20 { margin-top: 20px; }
.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-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; }
diff --git a/app/assets/stylesheets/framework/contextual-sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 1acde98c3ae..1acde98c3ae 100644
--- a/app/assets/stylesheets/framework/contextual-sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 691df098c70..1d7b0b602cc 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -736,10 +736,6 @@
}
}
-.droplab-item-ignore {
- pointer-events: none;
-}
-
.pika-single.animate-picker.is-bound,
.pika-single.animate-picker.is-bound.is-hidden {
/*
diff --git a/app/assets/stylesheets/framework/emoji-sprites.scss b/app/assets/stylesheets/framework/emoji_sprites.scss
index 0174e17b660..0174e17b660 100644
--- a/app/assets/stylesheets/framework/emoji-sprites.scss
+++ b/app/assets/stylesheets/framework/emoji_sprites.scss
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index be96c8ee964..2c30311b1c1 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -167,7 +167,7 @@ label {
.input-group {
.select2-container {
display: table-cell;
- width: 200px !important;
+ max-width: 180px;
}
.input-group-addon {
@@ -182,6 +182,7 @@ label {
.help-block {
margin-bottom: 0;
+ margin-top: #{$grid-size / 2};
}
.gl-field-error {
diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss
index db36e27fa74..db36e27fa74 100644
--- a/app/assets/stylesheets/framework/gitlab-theme.scss
+++ b/app/assets/stylesheets/framework/gitlab_theme.scss
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index dcd98cb522f..7e829826eba 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -255,8 +255,6 @@ ul.controls {
}
.author_link {
- display: inline-block;
-
.avatar-inline {
margin-left: 0;
margin-right: 0;
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index a12f28efce6..8604e753c18 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -63,10 +63,6 @@
}
}
- .project-stats {
- display: none;
- }
-
.group-buttons {
display: none;
}
diff --git a/app/assets/stylesheets/framework/page-header.scss b/app/assets/stylesheets/framework/page_header.scss
index 0c879f40930..0c879f40930 100644
--- a/app/assets/stylesheets/framework/page-header.scss
+++ b/app/assets/stylesheets/framework/page_header.scss
diff --git a/app/assets/stylesheets/framework/secondary-navigation-elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 17c31d6b184..17c31d6b184 100644
--- a/app/assets/stylesheets/framework/secondary-navigation-elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index dbee7073975..b40dcf93969 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -8,7 +8,7 @@
.select2-choice {
background: $white-light;
border-color: $input-border;
- height: 35px;
+ height: 34px;
padding: $gl-vert-padding $gl-input-padding;
font-size: $gl-font-size;
line-height: 1.42857143;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index d61809cb0a4..d1d98270ad9 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -3,7 +3,6 @@
transition: padding $sidebar-transition-duration;
.container-fluid {
- background: $white-light;
padding: 0 $gl-padding;
&.container-blank {
diff --git a/app/assets/stylesheets/framework/stacked-progress-bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss
index 4869cda73e5..528ba53a48b 100644
--- a/app/assets/stylesheets/framework/stacked-progress-bar.scss
+++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss
@@ -10,7 +10,7 @@
.status-neutral,
.status-red, {
height: 100%;
- min-width: 25px;
+ min-width: 30px;
padding: 0 5px;
font-size: $tooltip-font-size;
font-weight: normal;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index d0999e60e65..294c59f037f 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -296,7 +296,7 @@ body {
line-height: 1.3;
font-size: 1.25em;
font-weight: $gl-font-weight-bold;
- margin: 12px 7px;
+ margin: 12px 0;
}
h1,
@@ -333,6 +333,10 @@ a > code {
font-family: $monospace_font;
}
+.weight-normal {
+ font-weight: $gl-font-weight-normal;
+}
+
.commit-sha,
.ref-name {
@extend .monospace;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 25ee081ea9c..a5a8f6d2206 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -215,8 +215,8 @@ $tooltip-font-size: 12px;
*/
$gl-padding: 16px;
$gl-padding-8: 8px;
+$gl-padding-4: 4px;
$gl-col-padding: 15px;
-$gl-btn-padding: 10px;
$gl-input-padding: 10px;
$gl-vert-padding: 6px;
$gl-padding-top: 10px;
@@ -377,6 +377,10 @@ $inactive-badge-background: rgba(0, 0, 0, .08);
$btn-active-gray: #ececec;
$btn-active-gray-light: e4e7ed;
$btn-white-active: #848484;
+$gl-btn-padding: 10px;
+$gl-btn-line-height: 16px;
+$gl-btn-vert-padding: 8px;
+$gl-btn-horz-padding: 12px;
/*
* Badges
@@ -558,6 +562,7 @@ $jq-ui-default-color: #777;
/*
* Label
*/
+$label-font-size: 12px;
$label-padding: 7px;
$label-padding-modal: 10px;
$label-gray-bg: #f8fafc;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 17801ed5910..8b680c2dc52 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -196,17 +196,9 @@
@media (min-width: $screen-sm-min) {
font-size: 0;
- div {
- display: inline;
- }
-
.fa-spinner {
font-size: 12px;
}
-
- span {
- font-size: 6px;
- }
}
.ci-status-link {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 4eba05a492d..884665d35c7 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -121,6 +121,10 @@
width: 100%;
text-align: left;
}
+
+ .environment-child-row {
+ padding-left: 20px;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index da096354b5a..8871a069d5d 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -72,7 +72,6 @@
.label {
color: $gl-text-color;
- font-size: inherit;
}
iframe.twitter-share-button {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 759719a72da..0cf67734237 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -102,12 +102,11 @@
.issuable-show-labels {
a {
- margin-right: 5px;
margin-bottom: 5px;
display: inline-block;
.color-label {
- padding: 6px 10px;
+ padding: 4px $grid-size;
border-radius: $label-border-radius;
}
@@ -197,11 +196,18 @@
margin-left: 0;
}
+ a.edit-link:not([href]):hover {
+ color: rgba($avatar-border, .2);
+ }
+
+ .lock-edit, // uses same style, different js behaviour
.edit-link {
+ @extend .btn-blank;
color: $gl-text-color;
- &:not([href]):hover {
- color: rgba($avatar-border, .2);
+ &:hover {
+ text-decoration: underline;
+ color: $md-link-color;
}
}
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index c48e58af691..b9390450477 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -13,10 +13,20 @@
display: inline-block;
}
+ .issuable-meta {
+ .author_link {
+ display: inline-block;
+ }
+
+ .issuable-comments {
+ height: 18px;
+ }
+ }
+
.icon-merge-request-unmerged {
height: 13px;
margin-bottom: 3px;
- }
+ }
}
}
@@ -181,11 +191,6 @@ ul.related-merge-requests > li {
}
.create-mr-dropdown-wrap {
- .branch-message,
- .ref-message {
- display: none;
- }
-
.ref::selection {
color: $placeholder-text-color;
}
@@ -216,6 +221,17 @@ ul.related-merge-requests > li {
transform: translateY(0);
display: none;
margin-top: 4px;
+
+ // override dropdown item styles
+ .btn.btn-success {
+ @include btn-default;
+ @include btn-green;
+
+ border-style: solid;
+ border-width: 1px;
+ line-height: $line-height-base;
+ width: auto;
+ }
}
.create-merge-request-dropdown-toggle {
@@ -225,66 +241,6 @@ ul.related-merge-requests > li {
margin-left: 0;
}
}
-
- .droplab-item-ignore {
- pointer-events: auto;
- }
-
- .create-item {
- cursor: pointer;
- margin: 0 1px;
-
- &:hover,
- &:focus {
- background-color: $dropdown-item-hover-bg;
- color: $gl-text-color;
- }
- }
-
- li.divider {
- margin: 8px 10px;
- }
-
- li:not(.divider) {
- padding: 8px 9px;
-
- &:last-child {
- padding-bottom: 8px;
- }
-
- &.droplab-item-selected {
- .icon-container {
- i {
- visibility: visible;
- }
- }
-
- .description {
- display: block;
- }
- }
-
- &.droplab-item-ignore {
- padding-top: 8px;
- }
-
- .icon-container {
- float: left;
-
- i {
- visibility: hidden;
- }
- }
-
- .description {
- padding-left: 22px;
- }
-
- input,
- span {
- margin: 4px 0 0;
- }
- }
}
.discussion-reply-holder .note-edit-form {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index a72e654824e..0f49d15203b 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -105,13 +105,16 @@
}
.label {
- padding: 8px 12px;
- font-size: 14px;
+ padding: 4px $grid-size;
+ font-size: $label-font-size;
+ position: relative;
+ top: ($grid-size / 2);
}
}
.color-label {
- padding: 3px $label-padding;
+ padding: 0 $grid-size;
+ line-height: 16px;
border-radius: $label-border-radius;
}
@@ -302,10 +305,11 @@
}
.label-link {
- display: inline-block;
+ display: inline-flex;
vertical-align: top;
.label {
vertical-align: inherit;
+ font-size: $label-font-size;
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index ae8fa45a2d7..e5afa8fffcb 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -115,6 +115,10 @@
display: block;
margin-top: 7px;
+ .issue-link {
+ display: inline-block;
+ }
+
.issuable-number {
color: $gl-text-color-secondary;
margin-right: 5px;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index f10908c3630..42772f13155 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -320,14 +320,17 @@
}
}
-// Pipeline graph
-.pipeline-graph {
+.pipeline-tab-content {
width: 100%;
background-color: $gray-light;
padding: $gl-padding;
+ overflow: auto;
+}
+
+// Pipeline graph
+.pipeline-graph {
white-space: nowrap;
transition: max-height 0.3s, padding 0.3s;
- overflow: auto;
.stage-column-list,
.builds-container > ul {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index bf41005b6d5..85de0d8e70f 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -678,6 +678,9 @@ a.deploy-project-label {
}
}
+.project-empty-note-panel {
+ border-bottom: 1px solid $border-color;
+}
.project-stats {
font-size: 0;
@@ -686,11 +689,13 @@ a.deploy-project-label {
border-bottom: 1px solid $border-color;
.nav {
- padding-top: 12px;
- padding-bottom: 12px;
+ margin-top: $gl-padding-8;
+ margin-bottom: $gl-padding-8;
> li {
display: inline-block;
+ margin-top: $gl-padding-4;
+ margin-bottom: $gl-padding-4;
&:not(:last-child) {
margin-right: $gl-padding;
@@ -704,36 +709,32 @@ a.deploy-project-label {
float: right;
}
}
+ }
- > a {
- padding: 0;
- background-color: transparent;
- font-size: 14px;
- line-height: 29px;
- color: $notes-light-color;
+ .stat-text,
+ .stat-link {
+ padding: $gl-btn-vert-padding 0;
+ background-color: transparent;
+ font-size: $gl-font-size;
+ line-height: $gl-btn-line-height;
+ color: $notes-light-color;
+ }
- &:hover,
- &:focus {
- color: $gl-text-color;
- text-decoration: underline;
- }
+ .stat-link {
+ &:hover,
+ &:focus {
+ color: $gl-text-color;
+ text-decoration: underline;
}
}
- }
- li.missing {
- border: 1px dashed $border-gray-normal-dashed;
- border-radius: $border-radius-default;
-
- a {
- padding-left: 10px;
- padding-right: 10px;
- color: $notes-light-color;
- display: block;
+ .btn {
+ padding: $gl-btn-vert-padding $gl-btn-horz-padding;
+ line-height: $gl-btn-line-height;
}
- &:hover {
- background-color: $gray-normal;
+ .btn-missing {
+ @extend .btn-missing;
}
}
}
@@ -743,7 +744,7 @@ pre.light-well {
}
.git-empty {
- margin: 0 7px 7px;
+ margin-bottom: 7px;
h5 {
color: $gl-text-color;
@@ -895,6 +896,12 @@ pre.light-well {
}
}
+.project-tip-command {
+ > .input-group-btn:first-child {
+ width: auto;
+ }
+}
+
.protected-branches-list,
.protected-tags-list {
margin-bottom: 30px;
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index a79772ea37b..4b9824fab0c 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -128,7 +128,6 @@
.label {
color: $gl-text-color;
- font-size: inherit;
}
p {
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b04bfaf3e49..e6a41202f04 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -126,10 +126,15 @@ class ApplicationController < ActionController::Base
Ability.allowed?(object, action, subject)
end
- def access_denied!
+ def access_denied!(message = nil)
respond_to do |format|
- format.json { head :not_found }
- format.any { render "errors/access_denied", layout: "errors", status: 404 }
+ format.any { head :not_found }
+ format.html do
+ render "errors/access_denied",
+ layout: "errors",
+ status: 404,
+ locals: { message: message }
+ end
end
end
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index ee23ee0bcc3..352f12a89fd 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -55,7 +55,7 @@ module Boards
end
def issue
- @issue ||= issues_finder.execute.find(params[:id])
+ @issue ||= issues_finder.find(params[:id])
end
def filter_params
diff --git a/app/controllers/concerns/controller_with_cross_project_access_check.rb b/app/controllers/concerns/controller_with_cross_project_access_check.rb
new file mode 100644
index 00000000000..a45c3384578
--- /dev/null
+++ b/app/controllers/concerns/controller_with_cross_project_access_check.rb
@@ -0,0 +1,24 @@
+module ControllerWithCrossProjectAccessCheck
+ extend ActiveSupport::Concern
+
+ included do
+ extend Gitlab::CrossProjectAccess::ClassMethods
+ before_action :cross_project_check
+ end
+
+ def cross_project_check
+ if Gitlab::CrossProjectAccess.find_check(self)&.should_run?(self)
+ authorize_cross_project_page!
+ end
+ end
+
+ def authorize_cross_project_page!
+ return if can?(current_user, :read_cross_project)
+
+ rejection_message = _(
+ "This page is unavailable because you are not allowed to read information "\
+ "across multiple projects."
+ )
+ access_denied!(rejection_message)
+ end
+end
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 0d7ee06deb6..f7ba305a59f 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -103,7 +103,7 @@ module IssuableCollections
# @filter_params[:authorized_only] = true
end
- @filter_params.permit(IssuableFinder::VALID_PARAMS)
+ @filter_params.permit(finder_type.valid_params)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
@@ -146,7 +146,7 @@ module IssuableCollections
def finder
strong_memoize(:finder) do
- issuable_finder_for(@finder_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ issuable_finder_for(finder_type)
end
end
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
index 3ba1235cee0..3b11a373368 100644
--- a/app/controllers/concerns/issues_action.rb
+++ b/app/controllers/concerns/issues_action.rb
@@ -4,7 +4,6 @@ module IssuesAction
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def issues
- @finder_type = IssuesFinder
@issues = issuables_collection
.non_archived
.page(params[:page])
@@ -17,4 +16,11 @@ module IssuesAction
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
+
+ private
+
+ def finder_type
+ (super if defined?(super)) ||
+ (IssuesFinder if action_name == 'issues')
+ end
end
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
index a9cc13038bf..b70db99b157 100644
--- a/app/controllers/concerns/merge_requests_action.rb
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -4,8 +4,6 @@ module MergeRequestsAction
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def merge_requests
- @finder_type = MergeRequestsFinder
-
@merge_requests = issuables_collection.page(params[:page])
@issuable_meta_data = issuable_meta_data(@merge_requests, collection_type)
@@ -14,6 +12,11 @@ module MergeRequestsAction
private
+ def finder_type
+ (super if defined?(super)) ||
+ (MergeRequestsFinder if action_name == 'merge_requests')
+ end
+
def filter_params
super.merge(non_archived: true)
end
diff --git a/app/controllers/concerns/routable_actions.rb b/app/controllers/concerns/routable_actions.rb
index f745deb083c..0931bdf4c04 100644
--- a/app/controllers/concerns/routable_actions.rb
+++ b/app/controllers/concerns/routable_actions.rb
@@ -3,16 +3,20 @@ module RoutableActions
def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil)
routable = routable_klass.find_by_full_path(requested_full_path, follow_redirects: request.get?)
-
if routable_authorized?(routable, extra_authorization_proc)
ensure_canonical_path(routable, requested_full_path)
routable
else
- route_not_found
+ handle_not_found_or_authorized(routable)
nil
end
end
+ # This is overridden in gitlab-ee.
+ def handle_not_found_or_authorized(_routable)
+ route_not_found
+ end
+
def routable_authorized?(routable, extra_authorization_proc)
action = :"read_#{routable.class.to_s.underscore}"
return false unless can?(current_user, action, routable)
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 7ad79a1e56c..3dbfabcae8a 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -24,7 +24,7 @@ module UploadsActions
# - or redirect to its URL
#
def show
- return render_404 unless uploader.exists?
+ return render_404 unless uploader&.exists?
if uploader.file_storage?
disposition = uploader.image_or_video? ? 'inline' : 'attachment'
@@ -71,6 +71,9 @@ module UploadsActions
def build_uploader_from_params
uploader = uploader_class.new(model, secret: params[:secret])
+
+ return nil unless uploader.model_valid?
+
uploader.retrieve_from_store!(params[:filename])
uploader
end
diff --git a/app/controllers/dashboard/application_controller.rb b/app/controllers/dashboard/application_controller.rb
index 9d3d1c23c28..9fb5c525425 100644
--- a/app/controllers/dashboard/application_controller.rb
+++ b/app/controllers/dashboard/application_controller.rb
@@ -1,6 +1,10 @@
class Dashboard::ApplicationController < ApplicationController
+ include ControllerWithCrossProjectAccessCheck
+
layout 'dashboard'
+ requires_cross_project_access
+
private
def projects
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index 025769f512a..79f563bef86 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -1,6 +1,8 @@
class Dashboard::GroupsController < Dashboard::ApplicationController
include GroupTree
+ skip_cross_project_access_check :index
+
def index
groups = GroupsFinder.new(current_user, all_available: false).execute
render_group_tree(groups)
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index de9f8f9224a..4d4ac025f8c 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -4,6 +4,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
before_action :set_non_archived_param
before_action :default_sorting
+ skip_cross_project_access_check :index, :starred
def index
@projects = load_projects(params.merge(non_public: true)).page(params[:page])
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index 8dd91264451..0ba97e4fd59 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -1,4 +1,6 @@
class Dashboard::SnippetsController < Dashboard::ApplicationController
+ skip_cross_project_access_check :index
+
def index
@snippets = SnippetsFinder.new(
current_user,
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 96ce686c989..9f3bb60b4cc 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,10 +1,12 @@
class Groups::ApplicationController < ApplicationController
include RoutableActions
+ include ControllerWithCrossProjectAccessCheck
layout 'group'
skip_before_action :authenticate_user!
before_action :group
+ requires_cross_project_access
private
@@ -16,10 +18,6 @@ class Groups::ApplicationController < ApplicationController
@projects ||= GroupProjectsFinder.new(group: group, current_user: current_user).execute
end
- def group_merge_requests
- @group_merge_requests = MergeRequestsFinder.new(current_user, group_id: @group.id).execute
- end
-
def authorize_admin_group!
unless can?(current_user, :admin_group, group)
return render_404
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 735915abdaa..cc5ba5878f8 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,6 +1,8 @@
class Groups::AvatarsController < Groups::ApplicationController
before_action :authorize_admin_group!
+ skip_cross_project_access_check :destroy
+
def destroy
@group.remove_avatar!
@group.save
diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb
index b474f5d15ee..0e8125d6113 100644
--- a/app/controllers/groups/children_controller.rb
+++ b/app/controllers/groups/children_controller.rb
@@ -1,6 +1,7 @@
module Groups
class ChildrenController < Groups::ApplicationController
before_action :group
+ skip_cross_project_access_check :index
def index
parent = if params[:parent_id].present?
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 21e77431176..2c371e76313 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -6,6 +6,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access]
+ skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
+ :approve_access_request, :leave, :resend_invite,
+ :override
+
def index
@sort = params[:sort].presence || sort_value_name
@project = @group.projects.find(params[:project_id]) if params[:project_id]
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index 0142ad8278c..4bf6a2a3ad1 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -1,6 +1,7 @@
module Groups
module Settings
class CiCdController < Groups::ApplicationController
+ skip_cross_project_access_check :show
before_action :authorize_admin_pipeline!
def show
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index 913e13bf734..cb8771bc97e 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -2,6 +2,8 @@ module Groups
class VariablesController < Groups::ApplicationController
before_action :authorize_admin_build!
+ skip_cross_project_access_check :show, :update
+
def show
respond_to do |format|
format.json do
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 7d129c5dece..283c3e5f1e0 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -14,11 +14,16 @@ class GroupsController < Groups::ApplicationController
before_action :authorize_create_group!, only: [:new]
before_action :group_projects, only: [:projects, :activity, :issues, :merge_requests]
- before_action :group_merge_requests, only: [:merge_requests]
before_action :event_filter, only: [:activity]
before_action :user_actions, only: [:show, :subgroups]
+ skip_cross_project_access_check :index, :new, :create, :edit, :update,
+ :destroy, :projects
+ # When loading show as an atom feed, we render events that could leak cross
+ # project information
+ skip_cross_project_access_check :show, if: -> { request.format.html? }
+
layout :determine_layout
def index
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 9de0297ecfd..c84fc2d305d 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -2,26 +2,16 @@ class Import::BaseController < ApplicationController
private
def find_or_create_namespace(names, owner)
- return current_user.namespace if names == owner
- return current_user.namespace unless current_user.can_create_group?
-
names = params[:target_namespace].presence || names
- full_path_namespace = Namespace.find_by_full_path(names)
- return full_path_namespace if full_path_namespace
+ return current_user.namespace if names == owner
+
+ group = Groups::NestedCreateService.new(current_user, group_path: names).execute
- names.split('/').inject(nil) do |parent, name|
- begin
- namespace = Group.create!(name: name,
- path: name,
- owner: current_user,
- parent: parent)
- namespace.add_owner(current_user)
+ group.errors.any? ? current_user.namespace : group
+ rescue => e
+ Gitlab::AppLogger.error(e)
- namespace
- rescue ActiveRecord::RecordNotUnique, ActiveRecord::RecordInvalid
- Namespace.where(parent: parent).find_by_path_or_name(name)
- end
- end
+ current_user.namespace
end
end
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index 5ad1e116e4e..13ea736688d 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -37,24 +37,30 @@ class Import::BitbucketController < Import::BaseController
def create
bitbucket_client = Bitbucket::Client.new(credentials)
- @repo_id = params[:repo_id].to_s
- name = @repo_id.gsub('___', '/')
+ repo_id = params[:repo_id].to_s
+ name = repo_id.gsub('___', '/')
repo = bitbucket_client.repo(name)
- @project_name = params[:new_name].presence || repo.name
+ project_name = params[:new_name].presence || repo.name
repo_owner = repo.owner
repo_owner = current_user.username if repo_owner == bitbucket_client.user.username
namespace_path = params[:new_namespace].presence || repo_owner
+ target_namespace = find_or_create_namespace(namespace_path, current_user)
- @target_namespace = find_or_create_namespace(namespace_path, current_user)
-
- if current_user.can?(:create_projects, @target_namespace)
+ if current_user.can?(:create_projects, target_namespace)
# The token in a session can be expired, we need to get most recent one because
# Bitbucket::Connection class refreshes it.
session[:bitbucket_token] = bitbucket_client.connection.token
- @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, credentials).execute
+
+ project = Gitlab::BitbucketImport::ProjectCreator.new(repo, project_name, target_namespace, current_user, credentials).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project.errors.full_messages }, status: :unprocessable_entity
+ end
else
- render 'unauthorized'
+ render json: { errors: 'This namespace has already been taken! Please choose another one.' }, status: :unprocessable_entity
end
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 5df6bd34185..669eb31a995 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -58,17 +58,17 @@ class Import::FogbugzController < Import::BaseController
end
def create
- @repo_id = params[:repo_id]
- repo = client.repo(@repo_id)
+ repo = client.repo(params[:repo_id])
fb_session = { uri: session[:fogbugz_uri], token: session[:fogbugz_token] }
- @target_namespace = current_user.namespace
- @project_name = repo.name
-
- namespace = @target_namespace
-
umap = session[:fogbugz_user_map] || client.user_map
- @project = Gitlab::FogbugzImport::ProjectCreator.new(repo, fb_session, namespace, current_user, umap).execute
+ project = Gitlab::FogbugzImport::ProjectCreator.new(repo, fb_session, current_user.namespace, current_user, umap).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project.errors.full_messages }, status: :unprocessable_entity
+ end
end
private
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index b8ba7921613..69fb8121ded 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -36,16 +36,21 @@ class Import::GithubController < Import::BaseController
end
def create
- @repo_id = params[:repo_id].to_i
- repo = client.repo(@repo_id)
- @project_name = params[:new_name].presence || repo.name
+ repo = client.repo(params[:repo_id].to_i)
+ project_name = params[:new_name].presence || repo.name
namespace_path = params[:target_namespace].presence || current_user.namespace_path
- @target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path)
+ target_namespace = find_or_create_namespace(namespace_path, current_user.namespace_path)
- if can?(current_user, :create_projects, @target_namespace)
- @project = Gitlab::LegacyGithubImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, access_params, type: provider).execute
+ if can?(current_user, :create_projects, target_namespace)
+ project = Gitlab::LegacyGithubImport::ProjectCreator.new(repo, project_name, target_namespace, current_user, access_params, type: provider).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project.errors.full_messages }, status: :unprocessable_entity
+ end
else
- render 'unauthorized'
+ 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_controller.rb b/app/controllers/import/gitlab_controller.rb
index 407154e59a0..18f1d20f5a9 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -24,15 +24,19 @@ class Import::GitlabController < Import::BaseController
end
def create
- @repo_id = params[:repo_id].to_i
- repo = client.project(@repo_id)
- @project_name = repo['name']
- @target_namespace = find_or_create_namespace(repo['namespace']['path'], client.user['username'])
+ repo = client.project(params[:repo_id].to_i)
+ target_namespace = find_or_create_namespace(repo['namespace']['path'], client.user['username'])
- if current_user.can?(:create_projects, @target_namespace)
- @project = Gitlab::GitlabImport::ProjectCreator.new(repo, @target_namespace, current_user, access_params).execute
+ if current_user.can?(:create_projects, target_namespace)
+ project = Gitlab::GitlabImport::ProjectCreator.new(repo, target_namespace, current_user, access_params).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project.errors.full_messages }, status: :unprocessable_entity
+ end
else
- render 'unauthorized'
+ render json: { errors: 'This namespace has already been taken! Please choose another one.' }, status: :unprocessable_entity
end
end
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 7d7f13ce5d5..baa19fb383d 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -85,16 +85,16 @@ class Import::GoogleCodeController < Import::BaseController
end
def create
- @repo_id = params[:repo_id]
- repo = client.repo(@repo_id)
- @target_namespace = current_user.namespace
- @project_name = repo.name
-
- namespace = @target_namespace
-
+ repo = client.repo(params[:repo_id])
user_map = session[:google_code_user_map]
- @project = Gitlab::GoogleCodeImport::ProjectCreator.new(repo, namespace, current_user, user_map).execute
+ project = Gitlab::GoogleCodeImport::ProjectCreator.new(repo, current_user.namespace, current_user, user_map).execute
+
+ if project.persisted?
+ render json: ProjectSerializer.new.represent(project)
+ else
+ render json: { errors: project.errors.full_messages }, status: :unprocessable_entity
+ end
end
private
diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb
index 6a21a3f77ad..a1fe02dc852 100644
--- a/app/controllers/oauth/applications_controller.rb
+++ b/app/controllers/oauth/applications_controller.rb
@@ -1,5 +1,6 @@
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
include Gitlab::GonHelper
+ include Gitlab::Allowable
include PageLayoutHelper
include OauthApplications
@@ -8,6 +9,8 @@ class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_action :add_gon_variables
before_action :load_scopes, only: [:index, :create, :edit]
+ helper_method :can?
+
layout 'profile'
def index
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 45c66b63ea5..992c8ea6992 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -34,9 +34,9 @@ class Projects::AutocompleteSourcesController < Projects::ApplicationController
def target
case params[:type]&.downcase
when 'issue'
- IssuesFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:type_id])
+ IssuesFinder.new(current_user, project_id: @project.id).find_by(iid: params[:type_id])
when 'mergerequest'
- MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:type_id])
+ MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:type_id])
when 'commit'
@project.commit(params[:type_id])
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 35e67730a27..74c25505e36 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -133,7 +133,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def after_edit_path
- from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).execute.find_by(iid: params[:from_merge_request_iid])
+ from_merge_request = MergeRequestsFinder.new(current_user, project_id: @project.id).find_by(iid: params[:from_merge_request_iid])
if from_merge_request && @branch_name == @ref
diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
"##{hexdigest(@path)}"
diff --git a/app/controllers/projects/clusters/gcp_controller.rb b/app/controllers/projects/clusters/gcp_controller.rb
index 94d33b91562..6b0b22f8e73 100644
--- a/app/controllers/projects/clusters/gcp_controller.rb
+++ b/app/controllers/projects/clusters/gcp_controller.rb
@@ -39,12 +39,12 @@ class Projects::Clusters::GcpController < Projects::ApplicationController
def verify_billing
case google_project_billing_status
- when 'true'
+ when nil
+ flash.now[:alert] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
+ when false
+ flash.now[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" }
+ when true
return
- when 'false'
- flash[:alert] = _('Please <a href=%{link_to_billing} target="_blank" rel="noopener noreferrer">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again.').html_safe % { link_to_billing: "https://console.cloud.google.com/freetrial?utm_campaign=2018_cpanel&utm_source=gitlab&utm_medium=referral" }
- else
- flash[:alert] = _('We could not verify that one of your projects on GCP has billing enabled. Please try again.')
end
@cluster = ::Clusters::Cluster.new(create_params)
@@ -81,9 +81,7 @@ class Projects::Clusters::GcpController < Projects::ApplicationController
end
def google_project_billing_status
- Gitlab::Redis::SharedState.with do |redis|
- redis.get(CheckGcpProjectBillingWorker.redis_shared_state_key_for(token_in_session))
- end
+ CheckGcpProjectBillingWorker.get_billing_state(token_in_session)
end
def token_in_session
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 515cb08f1fc..73806454525 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -122,8 +122,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def referenced_merge_requests
- @merge_requests = @issue.referenced_merge_requests(current_user)
- @closed_by_merge_requests = @issue.closed_by_merge_requests(current_user)
+ @merge_requests, @closed_by_merge_requests = ::Issues::FetchReferencedMergeRequestsService.new(project, current_user).execute(issue)
respond_to do |format|
format.json do
@@ -244,9 +243,8 @@ class Projects::IssuesController < Projects::ApplicationController
Issues::UpdateService.new(project, current_user, update_params)
end
- def set_issuables_index
- @finder_type = IssuesFinder
- super
+ def finder_type
+ IssuesFinder
end
def whitelist_query_limiting
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index a5a2d54ba82..a90030a8312 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -75,7 +75,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def branch_to
@target_project = selected_target_project
- if params[:ref].present?
+ if @target_project && params[:ref].present?
@ref = params[:ref]
@commit = @target_project.commit(Gitlab::Git::BRANCH_REF_PREFIX + @ref)
end
@@ -85,7 +85,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def update_branches
@target_project = selected_target_project
- @target_branches = @target_project.repository.branch_names
+ @target_branches = @target_project ? @target_project.repository.branch_names : []
render layout: false
end
@@ -121,7 +121,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
@project
elsif params[:target_project_id].present?
MergeRequestTargetProjectFinder.new(current_user: current_user, source_project: @project)
- .execute.find(params[:target_project_id])
+ .find_by(id: params[:target_project_id])
else
@project.forked_from_project
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 8eed957d9fe..a1af125547c 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -323,9 +323,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
@target_branches = @merge_request.target_project.repository.branch_names
end
- def set_issuables_index
- @finder_type = MergeRequestsFinder
- super
+ def finder_type
+ MergeRequestsFinder
end
def check_user_can_push_to_source_branch!
diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb
index 15e77d854dc..b71f1e5fef4 100644
--- a/app/controllers/projects/pages_domains_controller.rb
+++ b/app/controllers/projects/pages_domains_controller.rb
@@ -3,7 +3,7 @@ class Projects::PagesDomainsController < Projects::ApplicationController
before_action :require_pages_enabled!
before_action :authorize_update_pages!, except: [:show]
- before_action :domain, only: [:show, :destroy]
+ before_action :domain, only: [:show, :destroy, :verify]
def show
end
@@ -12,11 +12,23 @@ class Projects::PagesDomainsController < Projects::ApplicationController
@domain = @project.pages_domains.new
end
+ def verify
+ result = VerifyPagesDomainService.new(@domain).execute
+
+ if result[:status] == :success
+ flash[:notice] = 'Successfully verified domain ownership'
+ else
+ flash[:alert] = 'Failed to verify domain ownership'
+ end
+
+ redirect_to project_pages_domain_path(@project, @domain)
+ end
+
def create
@domain = @project.pages_domains.create(pages_domain_params)
if @domain.valid?
- redirect_to project_pages_path(@project)
+ redirect_to project_pages_domain_path(@project, @domain)
else
render 'new'
end
@@ -46,6 +58,6 @@ class Projects::PagesDomainsController < Projects::ApplicationController
end
def domain
- @domain ||= @project.pages_domains.find_by(domain: params[:id].to_s)
+ @domain ||= @project.pages_domains.find_by!(domain: params[:id].to_s)
end
end
diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb
new file mode 100644
index 00000000000..b739d0f0f90
--- /dev/null
+++ b/app/controllers/projects/prometheus/metrics_controller.rb
@@ -0,0 +1,27 @@
+module Projects
+ module Prometheus
+ class MetricsController < Projects::ApplicationController
+ before_action :authorize_admin_project!
+
+ def active_common
+ respond_to do |format|
+ format.json do
+ matched_metrics = prometheus_service.matched_metrics || {}
+
+ if matched_metrics.any?
+ render json: matched_metrics
+ else
+ head :no_content
+ end
+ end
+ end
+ end
+
+ private
+
+ def prometheus_service
+ @prometheus_service ||= project.find_or_initialize_service('prometheus')
+ end
+ end
+ end
+end
diff --git a/app/controllers/projects/prometheus_controller.rb b/app/controllers/projects/prometheus_controller.rb
deleted file mode 100644
index 507468d7102..00000000000
--- a/app/controllers/projects/prometheus_controller.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-class Projects::PrometheusController < Projects::ApplicationController
- before_action :authorize_read_project!
- before_action :require_prometheus_metrics!
-
- def active_metrics
- respond_to do |format|
- format.json do
- matched_metrics = project.prometheus_service.matched_metrics || {}
-
- if matched_metrics.any?
- render json: matched_metrics
- else
- head :no_content
- end
- end
- end
- end
-
- private
-
- def require_prometheus_metrics!
- render_404 unless project.prometheus_service.present?
- end
-end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 72573e0765d..913689a1e74 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -45,7 +45,7 @@ class ProjectsController < Projects::ApplicationController
notice: _("Project '%{project_name}' was successfully created.") % { project_name: @project.name }
)
else
- render 'new'
+ render 'new', locals: { active_tab: ('import' if project_params[:import_url].present?) }
end
end
@@ -114,6 +114,8 @@ class ProjectsController < Projects::ApplicationController
respond_to do |format|
format.html do
@notification_setting = current_user.notification_settings_for(@project) if current_user
+ @project = @project.present(current_user: current_user)
+
render_landing_page
end
@@ -279,7 +281,6 @@ class ProjectsController < Projects::ApplicationController
@project_wiki = @project.wiki
@wiki_home = @project_wiki.find_page('home', params[:version_id])
elsif @project.feature_available?(:issues, current_user)
- @finder_type = IssuesFinder
@issues = issuables_collection.page(params[:page])
@collection_type = 'Issue'
@issuable_meta_data = issuable_meta_data(@issues, @collection_type)
@@ -289,6 +290,10 @@ class ProjectsController < Projects::ApplicationController
end
end
+ def finder_type
+ IssuesFinder
+ end
+
def determine_layout
if [:new, :create].include?(action_name.to_sym)
'application'
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index fbad9ba7db8..983f888b8ec 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -1,9 +1,14 @@
class SearchController < ApplicationController
- skip_before_action :authenticate_user!
-
+ include ControllerWithCrossProjectAccessCheck
include SearchHelper
include RendersCommits
+ skip_before_action :authenticate_user!
+ requires_cross_project_access if: -> do
+ search_term_present = params[:search].present? || params[:term].present?
+ search_term_present && !params[:project_id].present?
+ end
+
layout 'search'
def show
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 575ec5c20f0..956df4a0a16 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,6 +1,15 @@
class UsersController < ApplicationController
include RoutableActions
include RendersMemberAccess
+ include ControllerWithCrossProjectAccessCheck
+
+ requires_cross_project_access show: false,
+ groups: false,
+ projects: false,
+ contributed: false,
+ snippets: true,
+ calendar: false,
+ calendar_activities: true
skip_before_action :authenticate_user!
before_action :user, except: [:exists]
@@ -103,12 +112,7 @@ class UsersController < ApplicationController
end
def load_events
- # Get user activity feed for projects common for both users
- @events = user.recent_events
- .merge(projects_for_current_user)
- .references(:project)
- .with_associations
- .limit_recent(20, params[:offset])
+ @events = UserRecentEventsFinder.new(current_user, user, params).execute
Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
end
@@ -141,10 +145,6 @@ class UsersController < ApplicationController
).execute.page(params[:page])
end
- def projects_for_current_user
- ProjectsFinder.new(current_user: current_user).execute
- end
-
def build_canonical_path(user)
url_for(params.merge(username: user.to_param))
end
diff --git a/app/finders/autocomplete_users_finder.rb b/app/finders/autocomplete_users_finder.rb
index c3f5358b577..e8a03947f59 100644
--- a/app/finders/autocomplete_users_finder.rb
+++ b/app/finders/autocomplete_users_finder.rb
@@ -1,6 +1,12 @@
class AutocompleteUsersFinder
+ # The number of users to display in the results is hardcoded to 20, and
+ # pagination is not supported. This ensures that performance remains
+ # consistent and removes the need for implementing keyset pagination to ensure
+ # good performance.
+ LIMIT = 20
+
attr_reader :current_user, :project, :group, :search, :skip_users,
- :page, :per_page, :author_id, :params
+ :author_id, :params
def initialize(params:, current_user:, project:, group:)
@current_user = current_user
@@ -8,8 +14,6 @@ class AutocompleteUsersFinder
@group = group
@search = params[:search]
@skip_users = params[:skip_users]
- @page = params[:page]
- @per_page = params[:per_page]
@author_id = params[:author_id]
@params = params
end
@@ -20,7 +24,7 @@ class AutocompleteUsersFinder
items = items.reorder(:name)
items = items.search(search) if search.present?
items = items.where.not(id: skip_users) if skip_users.present?
- items = items.page(page).per(per_page)
+ items = items.limit(LIMIT)
if params[:todo_filter].present? && current_user
items = items.todo_authors(current_user.id, params[:todo_state_filter])
@@ -52,9 +56,13 @@ class AutocompleteUsersFinder
end
def users_from_project
- user_ids = project.team.users.pluck(:id)
- user_ids << author_id if author_id.present?
+ if author_id.present?
+ union = Gitlab::SQL::Union
+ .new([project.authorized_users, User.where(id: author_id)])
- User.where(id: user_ids)
+ User.from("(#{union.to_sql}) #{User.table_name}")
+ else
+ project.authorized_users
+ end
end
end
diff --git a/app/finders/concerns/finder_methods.rb b/app/finders/concerns/finder_methods.rb
new file mode 100644
index 00000000000..2e905fa5750
--- /dev/null
+++ b/app/finders/concerns/finder_methods.rb
@@ -0,0 +1,51 @@
+module FinderMethods
+ def find_by!(*args)
+ raise_not_found_unless_authorized execute.find_by!(*args)
+ end
+
+ def find_by(*args)
+ if_authorized execute.find_by(*args)
+ end
+
+ def find(*args)
+ raise_not_found_unless_authorized model.find(*args)
+ end
+
+ private
+
+ def raise_not_found_unless_authorized(result)
+ result = if_authorized(result)
+
+ raise ActiveRecord::RecordNotFound.new("Couldn't find #{model}") unless result
+
+ result
+ end
+
+ def if_authorized(result)
+ # Return the result if the finder does not perform authorization checks.
+ # this is currently the case in the `MilestoneFinder`
+ return result unless respond_to?(:current_user)
+
+ if can_read_object?(result)
+ result
+ else
+ nil
+ end
+ end
+
+ def can_read_object?(object)
+ # When there's no policy, we'll allow the read, this is for example the case
+ # for Todos
+ return true unless DeclarativePolicy.has_policy?(object)
+
+ model_name = object&.model_name || model.model_name
+
+ Ability.allowed?(current_user, :"read_#{model_name.singular}", object)
+ end
+
+ # This fetches the model from the `ActiveRecord::Relation` but does not
+ # actually execute the query.
+ def model
+ execute.model
+ end
+end
diff --git a/app/finders/concerns/finder_with_cross_project_access.rb b/app/finders/concerns/finder_with_cross_project_access.rb
new file mode 100644
index 00000000000..92bf98d7cd2
--- /dev/null
+++ b/app/finders/concerns/finder_with_cross_project_access.rb
@@ -0,0 +1,70 @@
+# Module to prepend into finders to specify wether or not the finder requires
+# cross project access
+#
+# This module depends on the finder implementing the following methods:
+#
+# - `#execute` should return an `ActiveRecord::Relation`
+# - `#current_user` the user that requires access (or nil)
+module FinderWithCrossProjectAccess
+ extend ActiveSupport::Concern
+ extend ::Gitlab::Utils::Override
+
+ prepended do
+ extend Gitlab::CrossProjectAccess::ClassMethods
+ end
+
+ override :execute
+ def execute(*args)
+ check = Gitlab::CrossProjectAccess.find_check(self)
+ original = super
+
+ return original unless check
+ return original if should_skip_cross_project_check || can_read_cross_project?
+
+ if check.should_run?(self)
+ original.model.none
+ else
+ original
+ end
+ end
+
+ # We can skip the cross project check for finding indivitual records.
+ # this would be handled by the `can?(:read_*, result)` call in `FinderMethods`
+ # itself.
+ override :find_by!
+ def find_by!(*args)
+ skip_cross_project_check { super }
+ end
+
+ override :find_by
+ def find_by(*args)
+ skip_cross_project_check { super }
+ end
+
+ override :find
+ def find(*args)
+ skip_cross_project_check { super }
+ end
+
+ private
+
+ attr_accessor :should_skip_cross_project_check
+
+ def skip_cross_project_check
+ self.should_skip_cross_project_check = true
+
+ yield
+ ensure
+ # The find could raise an `ActiveRecord::RecordNotFound`, after which we
+ # still want to re-enable the check.
+ self.should_skip_cross_project_check = false
+ end
+
+ def can_read_cross_project?
+ Ability.allowed?(current_user, :read_cross_project)
+ end
+
+ def can_read_project?(project)
+ Ability.allowed?(current_user, :read_project, project)
+ end
+end
diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb
index 46ecbaba73a..8676925a540 100644
--- a/app/finders/events_finder.rb
+++ b/app/finders/events_finder.rb
@@ -1,6 +1,10 @@
class EventsFinder
+ prepend FinderMethods
+ prepend FinderWithCrossProjectAccess
attr_reader :source, :params, :current_user
+ requires_cross_project_access unless: -> { source.is_a?(Project) }
+
# Used to filter Events
#
# Arguments:
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 0fe3000ca01..9dd6634b38f 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -21,36 +21,46 @@
# my_reaction_emoji: string
#
class IssuableFinder
+ prepend FinderWithCrossProjectAccess
+ include FinderMethods
include CreatedAtFilter
- NONE = '0'.freeze
+ requires_cross_project_access unless: -> { project? }
- SCALAR_PARAMS = %i[
- assignee_id
- assignee_username
- author_id
- author_username
- authorized_only
- due_date
- group_id
- iids
- label_name
- milestone_title
- my_reaction_emoji
- non_archived
- project_id
- scope
- search
- sort
- state
- include_subgroups
- ].freeze
- ARRAY_PARAMS = { label_name: [], iids: [], assignee_username: [] }.freeze
-
- VALID_PARAMS = (SCALAR_PARAMS + [ARRAY_PARAMS]).freeze
+ NONE = '0'.freeze
attr_accessor :current_user, :params
+ def self.scalar_params
+ @scalar_params ||= %i[
+ assignee_id
+ assignee_username
+ author_id
+ author_username
+ authorized_only
+ group_id
+ iids
+ label_name
+ milestone_title
+ my_reaction_emoji
+ non_archived
+ project_id
+ scope
+ search
+ sort
+ state
+ include_subgroups
+ ]
+ end
+
+ def self.array_params
+ @array_params ||= { label_name: [], iids: [], assignee_username: [] }
+ end
+
+ def self.valid_params
+ @valid_params ||= scalar_params + [array_params]
+ end
+
def initialize(current_user, params = {})
@current_user = current_user
@params = params
@@ -58,6 +68,15 @@ class IssuableFinder
def execute
items = init_collection
+ items = filter_items(items)
+
+ # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
+ items = by_project(items)
+
+ sort(items)
+ end
+
+ def filter_items(items)
items = by_scope(items)
items = by_created_at(items)
items = by_state(items)
@@ -65,24 +84,11 @@ class IssuableFinder
items = by_search(items)
items = by_assignee(items)
items = by_author(items)
- items = by_due_date(items)
items = by_non_archived(items)
items = by_iids(items)
items = by_milestone(items)
items = by_label(items)
- items = by_my_reaction_emoji(items)
-
- # Filtering by project HAS TO be the last because we use the project IDs yielded by the issuable query thus far
- items = by_project(items)
- sort(items)
- end
-
- def find(*params)
- execute.find(*params)
- end
-
- def find_by(*params)
- execute.find_by(*params)
+ by_my_reaction_emoji(items)
end
def row_count
@@ -114,10 +120,6 @@ class IssuableFinder
counts
end
- def find_by!(*params)
- execute.find_by!(*params)
- end
-
def group
return @group if defined?(@group)
@@ -396,42 +398,6 @@ class IssuableFinder
items
end
- def by_due_date(items)
- if due_date?
- if filter_by_no_due_date?
- items = items.without_due_date
- elsif filter_by_overdue?
- items = items.due_before(Date.today)
- elsif filter_by_due_this_week?
- items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week)
- elsif filter_by_due_this_month?
- items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month)
- end
- end
-
- items
- end
-
- def filter_by_no_due_date?
- due_date? && params[:due_date] == Issue::NoDueDate.name
- end
-
- def filter_by_overdue?
- due_date? && params[:due_date] == Issue::Overdue.name
- end
-
- def filter_by_due_this_week?
- due_date? && params[:due_date] == Issue::DueThisWeek.name
- end
-
- def filter_by_due_this_month?
- due_date? && params[:due_date] == Issue::DueThisMonth.name
- end
-
- def due_date?
- params[:due_date].present? && klass.column_names.include?('due_date')
- end
-
def label_names
if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 98831f5be4a..d65c620e75a 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -16,12 +16,17 @@
# sort: string
# my_reaction_emoji: string
# public_only: boolean
+# due_date: date or '0', '', 'overdue', 'week', or 'month'
#
class IssuesFinder < IssuableFinder
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER
+ def self.scalar_params
+ @scalar_params ||= super + [:due_date]
+ end
+
def klass
- Issue
+ Issue.includes(:author)
end
def with_confidentiality_access_check
@@ -52,6 +57,46 @@ class IssuesFinder < IssuableFinder
params.fetch(:public_only, false)
end
+ def filter_items(items)
+ by_due_date(super)
+ end
+
+ def by_due_date(items)
+ if due_date?
+ if filter_by_no_due_date?
+ items = items.without_due_date
+ elsif filter_by_overdue?
+ items = items.due_before(Date.today)
+ elsif filter_by_due_this_week?
+ items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week)
+ elsif filter_by_due_this_month?
+ items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month)
+ end
+ end
+
+ items
+ end
+
+ def filter_by_no_due_date?
+ due_date? && params[:due_date] == Issue::NoDueDate.name
+ end
+
+ def filter_by_overdue?
+ due_date? && params[:due_date] == Issue::Overdue.name
+ end
+
+ def filter_by_due_this_week?
+ due_date? && params[:due_date] == Issue::DueThisWeek.name
+ end
+
+ def filter_by_due_this_month?
+ due_date? && params[:due_date] == Issue::DueThisMonth.name
+ end
+
+ def due_date?
+ params[:due_date].present?
+ end
+
def user_can_see_all_confidential_issues?
return @user_can_see_all_confidential_issues if defined?(@user_can_see_all_confidential_issues)
diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb
index 1427cdaa382..5c9fce211ec 100644
--- a/app/finders/labels_finder.rb
+++ b/app/finders/labels_finder.rb
@@ -1,6 +1,10 @@
class LabelsFinder < UnionFinder
+ prepend FinderWithCrossProjectAccess
+ include FinderMethods
include Gitlab::Utils::StrongMemoize
+ requires_cross_project_access unless: -> { project? }
+
def initialize(current_user, params = {})
@current_user = current_user
@params = params
@@ -35,7 +39,7 @@ class LabelsFinder < UnionFinder
end
end
elsif only_group_labels?
- label_ids << Label.where(group_id: group.id)
+ label_ids << Label.where(group_id: group_ids)
else
label_ids << Label.where(group_id: projects.group_ids)
label_ids << Label.where(project_id: projects.select(:id))
@@ -55,10 +59,11 @@ class LabelsFinder < UnionFinder
items.where(title: title)
end
- def group
- strong_memoize(:group) do
+ def group_ids
+ strong_memoize(:group_ids) do
group = Group.find(params[:group_id])
- authorized_to_read_labels?(group) && group
+ groups = params[:include_ancestor_groups].present? ? group.self_and_ancestors : [group]
+ groups_user_can_read_labels(groups).map(&:id)
end
end
@@ -116,4 +121,10 @@ class LabelsFinder < UnionFinder
Ability.allowed?(current_user, :read_label, label_parent)
end
+
+ def groups_user_can_read_labels(groups)
+ DeclarativePolicy.user_scope do
+ groups.select { |group| authorized_to_read_labels?(group) }
+ end
+ end
end
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index af24045886e..4734d97b8c7 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -10,26 +10,59 @@ class MembersFinder
def execute
project_members = project.project_members
project_members = project_members.non_invite unless can?(current_user, :admin_project, project)
- wheres = ["members.id IN (#{project_members.select(:id).to_sql})"]
if group
- # We need `.where.not(user_id: nil)` here otherwise when a group has an
- # invitee, it would make the following query return 0 rows since a NULL
- # user_id would be present in the subquery
- # See http://stackoverflow.com/questions/129077/not-in-clause-and-null-values
- non_null_user_ids = project_members.where.not(user_id: nil).select(:user_id)
-
group_members = GroupMembersFinder.new(group).execute
- group_members = group_members.where.not(user_id: non_null_user_ids)
- group_members = group_members.non_invite unless can?(current_user, :admin_group, group)
+ group_members = group_members.non_invite
- wheres << "members.id IN (#{group_members.select(:id).to_sql})"
- end
+ union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false)
- Member.where(wheres.join(' OR '))
+ sql = distinct_on(union)
+
+ Member.includes(:user).from("(#{sql}) AS #{Member.table_name}")
+ else
+ project_members
+ end
end
def can?(*args)
Ability.allowed?(*args)
end
+
+ private
+
+ def distinct_on(union)
+ # We're interested in a list of members without duplicates by user_id.
+ # We prefer project members over group members, project members should go first.
+ if Gitlab::Database.postgresql?
+ <<~SQL
+ SELECT DISTINCT ON (user_id, invite_email) member_union.*
+ FROM (#{union.to_sql}) AS member_union
+ ORDER BY user_id,
+ invite_email,
+ CASE
+ WHEN type = 'ProjectMember' THEN 1
+ WHEN type = 'GroupMember' THEN 2
+ ELSE 3
+ END
+ SQL
+ else
+ # Older versions of MySQL do not support window functions (and DISTINCT ON is postgres-specific).
+ <<~SQL
+ SELECT t1.*
+ FROM (#{union.to_sql}) AS t1
+ JOIN (
+ SELECT
+ COALESCE(user_id, -1) AS user_id,
+ COALESCE(invite_email, 'NULL') AS invite_email,
+ MIN(CASE WHEN type = 'ProjectMember' THEN 1 WHEN type = 'GroupMember' THEN 2 ELSE 3 END) AS type_number
+ FROM
+ (#{union.to_sql}) AS t3
+ GROUP BY COALESCE(user_id, -1), COALESCE(invite_email, 'NULL')
+ ) AS t2 ON COALESCE(t1.user_id, -1) = t2.user_id
+ AND COALESCE(t1.invite_email, 'NULL') = t2.invite_email
+ AND CASE WHEN t1.type = 'ProjectMember' THEN 1 WHEN t1.type = 'GroupMember' THEN 2 ELSE 3 END = t2.type_number
+ SQL
+ end
+ end
end
diff --git a/app/finders/merge_request_target_project_finder.rb b/app/finders/merge_request_target_project_finder.rb
index 189eb3847eb..f358938344e 100644
--- a/app/finders/merge_request_target_project_finder.rb
+++ b/app/finders/merge_request_target_project_finder.rb
@@ -1,4 +1,6 @@
class MergeRequestTargetProjectFinder
+ include FinderMethods
+
attr_reader :current_user, :source_project
def initialize(current_user: nil, source_project:)
diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
index b4605fca193..f5d2b9f253a 100644
--- a/app/finders/milestones_finder.rb
+++ b/app/finders/milestones_finder.rb
@@ -8,6 +8,8 @@
# state - filters by state.
class MilestonesFinder
+ include FinderMethods
+
attr_reader :params, :project_ids, :group_ids
def initialize(params = {})
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 12157818bcd..33ee1e975b9 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -57,7 +57,7 @@ class NotesFinder
types = %w(commit issue merge_request snippet)
note_relations = types.map { |t| notes_for_type(t) }
note_relations.map! { |notes| search(notes) }
- UnionFinder.new.find_union(note_relations, Note)
+ UnionFinder.new.find_union(note_relations, Note.includes(:author))
end
def noteables_for_type(noteable_type)
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 4450766485f..a73c573736e 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -1,14 +1,30 @@
+# Snippets Finder
+#
+# Used to filter Snippets collections by a set of params
+#
+# Arguments.
+#
+# current_user - The current user, nil also can be used.
+# params:
+# visibility (integer) - Individual snippet visibility: Public(20), internal(10) or private(0).
+# project (Project) - Project related.
+# author (User) - Author related.
+#
+# params are optional
class SnippetsFinder < UnionFinder
- attr_accessor :current_user, :params
+ include Gitlab::Allowable
+ include FinderMethods
+
+ attr_accessor :current_user, :project, :params
def initialize(current_user, params = {})
@current_user = current_user
@params = params
+ @project = params[:project]
end
def execute
items = init_collection
- items = by_project(items)
items = by_author(items)
items = by_visibility(items)
@@ -18,25 +34,48 @@ class SnippetsFinder < UnionFinder
private
def init_collection
- items = Snippet.all
+ if project.present?
+ authorized_snippets_from_project
+ else
+ authorized_snippets
+ end
+ end
+
+ def authorized_snippets_from_project
+ if can?(current_user, :read_project_snippet, project)
+ if project.team.member?(current_user)
+ project.snippets
+ else
+ project.snippets.public_to_user(current_user)
+ end
+ else
+ Snippet.none
+ end
+ end
- accessible(items)
+ def authorized_snippets
+ Snippet.where(feature_available_projects.or(not_project_related))
+ .public_or_visible_to_user(current_user)
end
- def accessible(items)
- segments = []
- segments << items.public_to_user(current_user)
- segments << authorized_to_user(items) if current_user
+ def feature_available_projects
+ # Don't return any project related snippets if the user cannot read cross project
+ return table[:id].eq(nil) unless Ability.allowed?(current_user, :read_cross_project)
+
+ projects = Project.public_or_visible_to_user(current_user, use_where_in: false) do |part|
+ part.with_feature_available_for_user(:snippets, current_user)
+ end.select(:id)
+
+ arel_query = Arel::Nodes::SqlLiteral.new(projects.to_sql)
+ table[:project_id].in(arel_query)
+ end
- find_union(segments, Snippet.includes(:author))
+ def not_project_related
+ table[:project_id].eq(nil)
end
- def authorized_to_user(items)
- items.where(
- 'author_id = :author_id
- OR project_id IN (:project_ids)',
- author_id: current_user.id,
- project_ids: current_user.authorized_projects.select(:id))
+ def table
+ Snippet.arel_table
end
def by_visibility(items)
@@ -53,12 +92,6 @@ class SnippetsFinder < UnionFinder
items.where(author_id: params[:author].id)
end
- def by_project(items)
- return items unless params[:project]
-
- items.where(project_id: params[:project].id)
- end
-
def visibility_from_scope
case params[:scope].to_s
when 'are_private'
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 3502bf08971..edb17843002 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -13,6 +13,11 @@
#
class TodosFinder
+ prepend FinderWithCrossProjectAccess
+ include FinderMethods
+
+ requires_cross_project_access unless: -> { project? }
+
NONE = '0'.freeze
attr_accessor :current_user, :params
diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb
new file mode 100644
index 00000000000..6f7f7c30d92
--- /dev/null
+++ b/app/finders/user_recent_events_finder.rb
@@ -0,0 +1,33 @@
+# Get user activity feed for projects common for a user and a logged in user
+#
+# - current_user: The user viewing the events
+# - user: The user for which to load the events
+# - params:
+# - offset: The page of events to return
+class UserRecentEventsFinder
+ prepend FinderWithCrossProjectAccess
+ include FinderMethods
+
+ requires_cross_project_access
+
+ attr_reader :current_user, :target_user, :params
+
+ def initialize(current_user, target_user, params = {})
+ @current_user = current_user
+ @target_user = target_user
+ @params = params
+ end
+
+ def execute
+ target_user
+ .recent_events
+ .merge(projects_for_current_user)
+ .references(:project)
+ .with_associations
+ .limit_recent(20, params[:offset])
+ end
+
+ def projects_for_current_user
+ ProjectsFinder.new(current_user: current_user).execute
+ end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 6530327698b..475341cf9b1 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -34,7 +34,7 @@ module ApplicationHelper
def project_icon(project_id, options = {})
project =
- if project_id.is_a?(Project)
+ if project_id.respond_to?(:avatar_url)
project_id
else
Project.find_by_full_path(project_id)
@@ -68,18 +68,32 @@ module ApplicationHelper
end
end
- def avatar_icon(user_or_email = nil, size = nil, scale = 2, only_path: true)
- user =
- if user_or_email.is_a?(User)
- user_or_email
- else
- User.find_by_any_email(user_or_email.try(:downcase))
- end
+ # Takes both user and email and returns the avatar_icon by
+ # user (preferred) or email.
+ def avatar_icon_for(user = nil, email = nil, size = nil, scale = 2, only_path: true)
+ if user
+ avatar_icon_for_user(user, size, scale, only_path: only_path)
+ elsif email
+ avatar_icon_for_email(email, size, scale, only_path: only_path)
+ else
+ default_avatar
+ end
+ end
+
+ def avatar_icon_for_email(email = nil, size = nil, scale = 2, only_path: true)
+ user = User.find_by_any_email(email.try(:downcase))
+ if user
+ avatar_icon_for_user(user, size, scale, only_path: only_path)
+ else
+ gravatar_icon(email, size, scale)
+ end
+ end
+ def avatar_icon_for_user(user = nil, size = nil, scale = 2, only_path: true)
if user
user.avatar_url(size: size, only_path: only_path) || default_avatar
else
- gravatar_icon(user_or_email, size, scale)
+ gravatar_icon(nil, size, scale)
end
end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index e293b3ef329..ab68ecad2ba 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -199,6 +199,7 @@ module ApplicationSettingsHelper
:metrics_port,
:metrics_sample_interval,
:metrics_timeout,
+ :pages_domain_verification_enabled,
:password_authentication_enabled_for_web,
:password_authentication_enabled_for_git,
:performance_bar_allowed_group_id,
diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb
index be11d453898..21b6c0a8ad5 100644
--- a/app/helpers/avatars_helper.rb
+++ b/app/helpers/avatars_helper.rb
@@ -8,10 +8,22 @@ module AvatarsHelper
}))
end
+ def user_avatar_url_for(options = {})
+ if options[:url]
+ options[:url]
+ elsif options[:user]
+ avatar_icon_for_user(options[:user], options[:size])
+ else
+ avatar_icon_for_email(options[:user_email], options[:size])
+ end
+ end
+
def user_avatar_without_link(options = {})
avatar_size = options[:size] || 16
user_name = options[:user].try(:name) || options[:user_name]
- avatar_url = options[:url] || avatar_icon(options[:user] || options[:user_email], avatar_size)
+
+ avatar_url = user_avatar_url_for(options.merge(size: avatar_size))
+
has_tooltip = options[:has_tooltip].nil? ? true : options[:has_tooltip]
data_attributes = options[:data] || {}
css_class = %W[avatar s#{avatar_size}].push(*options[:css_class])
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index a6e1de6ffdc..0e806d16bc5 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -12,75 +12,42 @@ module BlobHelper
def edit_blob_path(project = @project, ref = @ref, path = @path, options = {})
project_edit_blob_path(project,
- tree_join(ref, path),
- options[:link_opts])
- end
-
- def edit_blob_link(project = @project, ref = @ref, path = @path, options = {})
- blob = options.delete(:blob)
- blob ||= project.repository.blob_at(ref, path) rescue nil
-
- return unless blob && blob.readable_text?
-
- common_classes = "btn js-edit-blob #{options[:extra_class]}"
-
- if !on_top_of_branch?(project, ref)
- button_tag 'Edit', class: "#{common_classes} disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' }
- # This condition applies to anonymous or users who can edit directly
- elsif !current_user || (current_user && can_modify_blob?(blob, project, ref))
- link_to 'Edit', edit_blob_path(project, ref, path, options), class: "#{common_classes} btn-sm"
- elsif current_user && can?(current_user, :fork_project, project)
- continue_params = {
- to: edit_blob_path(project, ref, path, options),
- notice: edit_in_new_fork_notice,
- notice_now: edit_in_new_fork_notice_now
- }
- fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params)
-
- button_tag 'Edit',
- class: "#{common_classes} js-edit-blob-link-fork-toggler",
- data: { action: 'edit', fork_path: fork_path }
- end
+ tree_join(ref, path),
+ options[:link_opts])
end
def ide_edit_path(project = @project, ref = @ref, path = @path, options = {})
"#{ide_path}/project#{edit_blob_path(project, ref, path, options)}"
end
- def ide_edit_text
- "#{_('Web IDE')}"
- end
+ def edit_blob_button(project = @project, ref = @ref, path = @path, options = {})
+ return unless blob = readable_blob(options, path, project, ref)
- def ide_blob_link(project = @project, ref = @ref, path = @path, options = {})
- return unless show_new_ide?
+ common_classes = "btn js-edit-blob #{options[:extra_class]}"
- blob = options.delete(:blob)
- blob ||= project.repository.blob_at(ref, path) rescue nil
+ edit_button_tag(blob,
+ common_classes,
+ _('Edit'),
+ edit_blob_path(project, ref, path, options),
+ project,
+ ref)
+ end
- return unless blob && blob.readable_text?
+ def ide_edit_button(project = @project, ref = @ref, path = @path, options = {})
+ return unless show_new_ide?
+ return unless blob = readable_blob(options, path, project, ref)
common_classes = "btn js-edit-ide #{options[:extra_class]}"
- if !on_top_of_branch?(project, ref)
- button_tag ide_edit_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' }
- # This condition applies to anonymous or users who can edit directly
- elsif current_user && can_modify_blob?(blob, project, ref)
- link_to ide_edit_text, ide_edit_path(project, ref, path, options), class: "#{common_classes} btn-sm"
- elsif current_user && can?(current_user, :fork_project, project)
- continue_params = {
- to: ide_edit_path(project, ref, path, options),
- notice: edit_in_new_fork_notice,
- notice_now: edit_in_new_fork_notice_now
- }
- fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params)
-
- button_tag ide_edit_text,
- class: common_classes,
- data: { fork_path: fork_path }
- end
+ edit_button_tag(blob,
+ common_classes,
+ _('Web IDE'),
+ ide_edit_path(project, ref, path, options),
+ project,
+ ref)
end
- def modify_file_link(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
+ def modify_file_button(project = @project, ref = @ref, path = @path, label:, action:, btn_class:, modal_type:)
return unless current_user
blob = project.repository.blob_at(ref, path) rescue nil
@@ -96,21 +63,12 @@ module BlobHelper
elsif can_modify_blob?(blob, project, ref)
button_tag label, class: "#{common_classes}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal'
elsif can?(current_user, :fork_project, project)
- continue_params = {
- to: request.fullpath,
- notice: edit_in_new_fork_notice + " Try to #{action} this file again.",
- notice_now: edit_in_new_fork_notice_now
- }
- fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: continue_params)
-
- button_tag label,
- class: "#{common_classes} js-edit-blob-link-fork-toggler",
- data: { action: action, fork_path: fork_path }
+ edit_fork_button_tag(common_classes, project, label, edit_modify_file_fork_params(action), action)
end
end
def replace_blob_link(project = @project, ref = @ref, path = @path)
- modify_file_link(
+ modify_file_button(
project,
ref,
path,
@@ -122,7 +80,7 @@ module BlobHelper
end
def delete_blob_link(project = @project, ref = @ref, path = @path)
- modify_file_link(
+ modify_file_button(
project,
ref,
path,
@@ -332,4 +290,55 @@ module BlobHelper
options
end
+
+ def readable_blob(options, path, project, ref)
+ blob = options.delete(:blob)
+ blob ||= project.repository.blob_at(ref, path) rescue nil
+
+ blob if blob&.readable_text?
+ end
+
+ def edit_blob_fork_params(path)
+ {
+ to: path,
+ notice: edit_in_new_fork_notice,
+ notice_now: edit_in_new_fork_notice_now
+ }
+ end
+
+ def edit_modify_file_fork_params(action)
+ {
+ to: request.fullpath,
+ notice: edit_in_new_fork_notice_action(action),
+ notice_now: edit_in_new_fork_notice_now
+ }
+ end
+
+ def edit_fork_button_tag(common_classes, project, label, params, action = 'edit')
+ fork_path = project_forks_path(project, namespace_key: current_user.namespace.id, continue: params)
+
+ button_tag label,
+ class: "#{common_classes} js-edit-blob-link-fork-toggler",
+ data: { action: action, fork_path: fork_path }
+ end
+
+ def edit_disabled_button_tag(button_text, common_classes)
+ button_tag(button_text, class: "#{common_classes} disabled has-tooltip", title: _('You can only edit files when you are on a branch'), data: { container: 'body' })
+ end
+
+ def edit_link_tag(link_text, edit_path, common_classes)
+ link_to link_text, edit_path, class: "#{common_classes} btn-sm"
+ end
+
+ def edit_button_tag(blob, common_classes, text, edit_path, project, ref)
+ if !on_top_of_branch?(project, ref)
+ edit_disabled_button_tag(text, common_classes)
+ # This condition only applies to users who are logged in
+ # Web IDE (Beta) requires the user to have this feature enabled
+ elsif !current_user || (current_user && can_modify_blob?(blob, project, ref))
+ edit_link_tag(text, edit_path, common_classes)
+ elsif current_user && can?(current_user, :fork_project, project)
+ edit_fork_button_tag(common_classes, project, text, edit_blob_fork_params(edit_path))
+ end
+ end
end
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index 2641a98e29e..00b9a0e00eb 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -10,12 +10,6 @@ module BranchesHelper
project_branches_path(@project, @id, options)
end
- def can_push_branch?(project, branch_name)
- return false unless project.repository.branch_exists?(branch_name)
-
- ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
- end
-
def project_branches
options_for_select(@project.repository.branch_names, @project.default_branch)
end
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index c25b54eadc6..19aa55a8d49 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -6,4 +6,28 @@ module DashboardHelper
def assigned_mrs_dashboard_path
merge_requests_dashboard_path(assignee_id: current_user.id)
end
+
+ def dashboard_nav_links
+ @dashboard_nav_links ||= get_dashboard_nav_links
+ end
+
+ def dashboard_nav_link?(link)
+ dashboard_nav_links.include?(link)
+ end
+
+ def any_dashboard_nav_link?(links)
+ links.any? { |link| dashboard_nav_link?(link) }
+ end
+
+ private
+
+ def get_dashboard_nav_links
+ links = [:projects, :groups, :snippets]
+
+ if can?(current_user, :read_cross_project)
+ links += [:activity, :milestones]
+ end
+
+ links
+ end
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 0f5fc2823a3..b5ca39711bc 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -160,7 +160,7 @@ module DiffHelper
end
def diff_file_changed_icon(diff_file)
- if diff_file.deleted_file? || diff_file.renamed_file?
+ if diff_file.deleted_file?
"file-deletion"
elsif diff_file.new_file?
"file-addition"
diff --git a/app/helpers/explore_helper.rb b/app/helpers/explore_helper.rb
index b981a1e8242..f062a91a166 100644
--- a/app/helpers/explore_helper.rb
+++ b/app/helpers/explore_helper.rb
@@ -25,8 +25,24 @@ module ExploreHelper
controller.class.name.split("::").first == "Explore"
end
+ def explore_nav_links
+ @explore_nav_links ||= get_explore_nav_links
+ end
+
+ def explore_nav_link?(link)
+ explore_nav_links.include?(link)
+ end
+
+ def any_explore_nav_link?(links)
+ links.any? { |link| explore_nav_link?(link) }
+ end
+
private
+ def get_explore_nav_links
+ [:projects, :groups, :snippets]
+ end
+
def request_path_with_options(options = {})
request.path + "?#{options.to_param}"
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 23de3590b93..7910de73c52 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -3,6 +3,14 @@ module GroupsHelper
%w[groups#projects groups#edit ci_cd#show ldap_group_links#index hooks#index audit_events#index pipeline_quota#index]
end
+ def group_sidebar_links
+ @group_sidebar_links ||= get_group_sidebar_links
+ end
+
+ def group_sidebar_link?(link)
+ group_sidebar_links.include?(link)
+ end
+
def can_change_group_visibility_level?(group)
can?(current_user, :change_visibility_level, group)
end
@@ -11,6 +19,20 @@ module GroupsHelper
can?(current_user, :change_share_with_group_lock, group)
end
+ def group_issues_count(state:)
+ IssuesFinder
+ .new(current_user, group_id: @group.id, state: state, non_archived: true, include_subgroups: true)
+ .execute
+ .count
+ end
+
+ def group_merge_requests_count(state:)
+ MergeRequestsFinder
+ .new(current_user, group_id: @group.id, state: state, non_archived: true, include_subgroups: true)
+ .execute
+ .count
+ end
+
def group_icon(group, options = {})
img_path = group_icon_url(group, options)
image_tag img_path, options
@@ -69,10 +91,6 @@ module GroupsHelper
end
end
- def group_issues(group)
- IssuesFinder.new(current_user, group_id: group.id).execute
- end
-
def remove_group_message(group)
_("You are going to remove %{group_name}. Removed groups CANNOT be restored! Are you ABSOLUTELY sure?") %
{ group_name: group.name }
@@ -107,6 +125,20 @@ module GroupsHelper
private
+ def get_group_sidebar_links
+ links = [:overview, :group_members]
+
+ if can?(current_user, :read_cross_project)
+ links += [:activity, :issues, :labels, :milestones, :merge_requests]
+ end
+
+ if can?(current_user, :admin_group, @group)
+ links << :settings
+ end
+
+ links
+ end
+
def group_title_link(group, hidable: false, show_avatar: false, for_dropdown: false)
link_to(group_path(group), class: "group-path #{'breadcrumb-item-text' unless for_dropdown} js-breadcrumb-item-text #{'hidable' if hidable}") do
output =
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 7cd84fe69c9..44ecc2212f2 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -234,7 +234,7 @@ module IssuablesHelper
data.merge!(updated_at_by(issuable))
- data.to_json
+ data
end
def updated_at_by(issuable)
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index 64cd3032780..0f25d401406 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -47,27 +47,6 @@ module IssuesHelper
end
end
- def milestone_options(object)
- milestones = object.project.milestones.active.reorder(due_date: :asc, title: :asc).to_a
- milestones.unshift(object.milestone) if object.milestone.present? && object.milestone.closed?
- milestones.unshift(Milestone::None)
-
- options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id)
- end
-
- def project_options(issuable, current_user, ability: :read_project)
- projects = current_user.authorized_projects.order_id_desc
- projects = projects.select do |project|
- current_user.can?(ability, project)
- end
-
- no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project')
- projects.unshift(no_project)
- projects.delete(issuable.project)
-
- options_from_collection_for_select(projects, :id, :name_with_namespace)
- end
-
def status_box_class(item)
if item.try(:expired?)
'status-box-expired'
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index b78d3072186..40ca666f1bf 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -33,7 +33,7 @@ module NamespacesHelper
if namespace.is_a?(Group)
group_icon(namespace)
else
- avatar_icon(namespace.owner.email, size)
+ avatar_icon_for_user(namespace.owner, size)
end
end
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 680ea96a556..56c88e6eab0 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -1,4 +1,12 @@
module NavHelper
+ def header_links
+ @header_links ||= get_header_links
+ end
+
+ def header_link?(link)
+ header_links.include?(link)
+ end
+
def page_with_sidebar_class
class_name = page_gutter_class
class_name << 'page-with-contextual-sidebar' if defined?(@left_sidebar) && @left_sidebar
@@ -38,4 +46,28 @@ module NavHelper
class_names
end
+
+ private
+
+ def get_header_links
+ links = if current_user
+ [:user_dropdown]
+ else
+ [:sign_in]
+ end
+
+ if can?(current_user, :read_cross_project)
+ links += [:issues, :merge_requests, :todos] if current_user.present?
+ end
+
+ if @project&.persisted? || can?(current_user, :read_cross_project)
+ links << :search
+ end
+
+ if session[:impersonator_id]
+ links << :admin_impersonation
+ end
+
+ links
+ end
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index aaee6eaeedd..373dfd457f7 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -48,30 +48,4 @@ module PreferencesHelper
def user_color_scheme
Gitlab::ColorSchemes.for_user(current_user).css_class
end
-
- def default_project_view
- return anonymous_project_view unless current_user
-
- user_view = current_user.project_view
-
- if can?(current_user, :download_code, @project)
- user_view
- elsif user_view == "activity"
- "activity"
- elsif can?(current_user, :read_wiki, @project)
- "wiki"
- elsif @project.feature_available?(:issues, current_user)
- "projects/issues/issues"
- else
- "customize_workflow"
- end
- end
-
- def anonymous_project_view
- if !@project.empty_repo? && can?(current_user, :download_code, @project)
- 'files'
- else
- 'activity'
- end
- end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 6512617a02d..cc1c69a1999 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -19,7 +19,7 @@ module ProjectsHelper
classes = %W[avatar avatar-inline s#{opts[:size]}]
classes << opts[:avatar_class] if opts[:avatar_class]
- avatar = avatar_icon(author, opts[:size])
+ avatar = avatar_icon_for_user(author, opts[:size])
src = opts[:lazy_load] ? nil : avatar
image_tag(src, width: opts[:size], class: classes, alt: '', "data-src" => avatar)
@@ -153,11 +153,6 @@ module ProjectsHelper
end
end
- def license_short_name(project)
- license = project.repository.license
- license&.nickname || license&.name || 'LICENSE'
- end
-
def last_push_event
current_user&.recent_push(@project)
end
@@ -213,6 +208,7 @@ module ProjectsHelper
controller.controller_name,
controller.action_name,
Gitlab::CurrentSettings.cache_key,
+ "cross-project:#{can?(current_user, :read_cross_project)}",
'v2.5'
]
@@ -265,6 +261,17 @@ module ProjectsHelper
!!(params[:personal] || params[:name] || any_projects?(projects))
end
+ def push_to_create_project_command(user = current_user)
+ repository_url =
+ if Gitlab::CurrentSettings.current_application_settings.enabled_git_access_protocol == 'http'
+ user_url(user)
+ else
+ Gitlab.config.gitlab_shell.ssh_path_prefix + user.username
+ end
+
+ "git push --set-upstream #{repository_url}/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)"
+ end
+
private
def repo_children_classes(field)
@@ -296,6 +303,10 @@ module ProjectsHelper
nav_tabs << :pipelines
end
+ if project.external_issue_tracker
+ nav_tabs << :external_issue_tracker
+ end
+
tab_ability_map.each do |tab, ability|
if can?(current_user, ability, project)
nav_tabs << tab
@@ -386,55 +397,6 @@ module ProjectsHelper
end
end
- def add_special_file_path(project, file_name:, commit_message: nil, branch_name: nil, context: nil)
- commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name }
- project_new_blob_path(
- project,
- project.default_branch || 'master',
- file_name: file_name,
- commit_message: commit_message,
- branch_name: branch_name,
- context: context
- )
- end
-
- def add_koding_stack_path(project)
- project_new_blob_path(
- project,
- project.default_branch || 'master',
- file_name: '.koding.yml',
- commit_message: "Add Koding stack script",
- content: <<-CONTENT.strip_heredoc
- provider:
- aws:
- access_key: '${var.aws_access_key}'
- secret_key: '${var.aws_secret_key}'
- resource:
- aws_instance:
- #{project.path}-vm:
- instance_type: t2.nano
- user_data: |-
-
- # Created by GitLab UI for :>
-
- echo _KD_NOTIFY_@Installing Base packages...@
-
- apt-get update -y
- apt-get install git -y
-
- echo _KD_NOTIFY_@Cloning #{project.name}...@
-
- export KODING_USER=${var.koding_user_username}
- export REPO_URL=#{root_url}${var.koding_queryString_repo}.git
- export BRANCH=${var.koding_queryString_branch}
-
- sudo -i -u $KODING_USER git clone $REPO_URL -b $BRANCH
-
- echo _KD_NOTIFY_@#{project.name} cloned.@
- CONTENT
- )
- end
-
def koding_project_url(project = nil, branch = nil, sha = nil)
if project
import_path = "/Home/Stacks/import"
@@ -451,36 +413,6 @@ module ProjectsHelper
Gitlab::CurrentSettings.koding_url
end
- def contribution_guide_path(project)
- if project && contribution_guide = project.repository.contribution_guide
- project_blob_path(
- project,
- tree_join(project.default_branch,
- contribution_guide.name)
- )
- end
- end
-
- def readme_path(project)
- filename_path(project, :readme)
- end
-
- def changelog_path(project)
- filename_path(project, :changelog)
- end
-
- def license_path(project)
- filename_path(project, :license_blob)
- end
-
- def version_path(project)
- filename_path(project, :version)
- end
-
- def ci_configuration_path(project)
- filename_path(project, :gitlab_ci_yml)
- end
-
def project_wiki_path_with_version(proj, page, version, is_newest)
url_params = is_newest ? {} : { version_id: version }
project_wiki_path(proj, page, url_params)
@@ -506,15 +438,6 @@ module ProjectsHelper
@ref || @repository.try(:root_ref)
end
- def filename_path(project, filename)
- if project && blob = project.repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
- project_blob_path(
- project,
- tree_join(project.default_branch, blob.name)
- )
- end
- end
-
def sanitize_repo_path(project, message)
return '' unless message.present?
@@ -604,4 +527,8 @@ module ProjectsHelper
project_find_file_path(@project, ref)
end
+
+ def can_show_last_commit_in_list?(project)
+ can?(current_user, :read_cross_project) && project.commit
+ end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index d39cac0f510..f6a6d9bebde 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -55,7 +55,9 @@ module TreeHelper
def tree_edit_branch(project = @project, ref = @ref)
return unless can_edit_tree?(project, ref)
- if can_push_branch?(project, ref)
+ project = project.present(current_user: current_user)
+
+ if project.can_current_user_push_to_branch?(ref)
ref
else
project = tree_edit_project(project)
@@ -81,6 +83,10 @@ module TreeHelper
" A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
+ def edit_in_new_fork_notice_action(action)
+ edit_in_new_fork_notice + " Try to #{action} this file again."
+ end
+
def commit_in_fork_help
"A new branch will be created in your fork and a new merge request will be started."
end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
index b5f54d3e154..01af68088df 100644
--- a/app/helpers/users_helper.rb
+++ b/app/helpers/users_helper.rb
@@ -14,4 +14,18 @@ module UsersHelper
content_tag(:strong) { user.unconfirmed_email } + h('.') +
content_tag(:p) { confirmation_link }
end
+
+ def profile_tabs
+ @profile_tabs ||= get_profile_tabs
+ end
+
+ def profile_tab?(tab)
+ profile_tabs.include?(tab)
+ end
+
+ private
+
+ def get_profile_tabs
+ [:activity, :groups, :contributed, :projects, :snippets]
+ end
end
diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb
index 9d071f2d59a..8bcced70d63 100644
--- a/app/helpers/webpack_helper.rb
+++ b/app/helpers/webpack_helper.rb
@@ -7,17 +7,24 @@ module WebpackHelper
def webpack_controller_bundle_tags
bundles = []
- segments = [*controller.controller_path.split('/'), controller.action_name].compact
- until segments.empty?
+ action = case controller.action_name
+ when 'create' then 'new'
+ when 'update' then 'edit'
+ else controller.action_name
+ end
+
+ route = [*controller.controller_path.split('/'), action].compact
+
+ until route.empty?
begin
- asset_paths = gitlab_webpack_asset_paths("pages.#{segments.join('.')}", extension: 'js')
+ asset_paths = gitlab_webpack_asset_paths("pages.#{route.join('.')}", extension: 'js')
bundles.unshift(*asset_paths)
rescue Webpack::Rails::Manifest::EntryPointMissingError
# no bundle exists for this path
end
- segments.pop
+ route.pop
end
javascript_include_tag(*bundles)
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index d76c61c369f..75cf56a51f2 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -7,18 +7,11 @@ module Emails
helper_method :member_source, :member
end
- def member_access_requested_email(member_source_type, member_id)
+ def member_access_requested_email(member_source_type, member_id, recipient_notification_email)
@member_source_type = member_source_type
@member_id = member_id
- admins = member_source.members.owners_and_masters.pluck(:notification_email)
- # A project in a group can have no explicit owners/masters, in that case
- # we fallbacks to the group's owners/masters.
- if admins.empty? && member_source.respond_to?(:group) && member_source.group
- admins = member_source.group.members.owners_and_masters.pluck(:notification_email)
- end
-
- mail(to: admins,
+ mail(to: recipient_notification_email,
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
end
diff --git a/app/mailers/emails/pages_domains.rb b/app/mailers/emails/pages_domains.rb
new file mode 100644
index 00000000000..0027dfdc36b
--- /dev/null
+++ b/app/mailers/emails/pages_domains.rb
@@ -0,0 +1,43 @@
+module Emails
+ module PagesDomains
+ def pages_domain_enabled_email(domain, recipient)
+ @domain = domain
+ @project = domain.project
+
+ mail(
+ to: recipient.notification_email,
+ subject: subject("GitLab Pages domain '#{domain.domain}' has been enabled")
+ )
+ end
+
+ def pages_domain_disabled_email(domain, recipient)
+ @domain = domain
+ @project = domain.project
+
+ mail(
+ to: recipient.notification_email,
+ subject: subject("GitLab Pages domain '#{domain.domain}' has been disabled")
+ )
+ end
+
+ def pages_domain_verification_succeeded_email(domain, recipient)
+ @domain = domain
+ @project = domain.project
+
+ mail(
+ to: recipient.notification_email,
+ subject: subject("Verification succeeded for GitLab Pages domain '#{domain.domain}'")
+ )
+ end
+
+ def pages_domain_verification_failed_email(domain, recipient)
+ @domain = domain
+ @project = domain.project
+
+ mail(
+ to: recipient.notification_email,
+ subject: subject("ACTION REQUIRED: Verification failed for GitLab Pages domain '#{domain.domain}'")
+ )
+ end
+ end
+end
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index eade0fe278f..45d4fb451d8 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -5,6 +5,7 @@ class Notify < BaseMailer
include Emails::Issues
include Emails::MergeRequests
include Emails::Notes
+ include Emails::PagesDomains
include Emails::Projects
include Emails::Profile
include Emails::Pipelines
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 0b6bcbde5d9..6dae49f38dc 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -22,12 +22,30 @@ class Ability
#
# issues - The issues to reduce down to those readable by the user.
# user - The User for which to check the issues
- def issues_readable_by_user(issues, user = nil)
+ # filters - A hash of abilities and filters to apply if the user lacks this
+ # ability
+ def issues_readable_by_user(issues, user = nil, filters: {})
+ issues = apply_filters_if_needed(issues, user, filters)
+
DeclarativePolicy.user_scope do
issues.select { |issue| issue.visible_to_user?(user) }
end
end
+ # Returns an Array of MergeRequests that can be read by the given user.
+ #
+ # merge_requests - MRs out of which to collect mr's readable by the user.
+ # user - The User for which to check the merge_requests
+ # filters - A hash of abilities and filters to apply if the user lacks this
+ # ability
+ def merge_requests_readable_by_user(merge_requests, user = nil, filters: {})
+ merge_requests = apply_filters_if_needed(merge_requests, user, filters)
+
+ DeclarativePolicy.user_scope do
+ merge_requests.select { |mr| allowed?(user, :read_merge_request, mr) }
+ end
+ end
+
def can_edit_note?(user, note)
allowed?(user, :edit_note, note)
end
@@ -53,5 +71,15 @@ class Ability
cache = RequestStore.active? ? RequestStore : {}
DeclarativePolicy.policy_for(user, subject, cache: cache)
end
+
+ private
+
+ def apply_filters_if_needed(elements, user, filters)
+ filters.each do |ability, filter|
+ elements = filter.call(elements) unless allowed?(user, ability)
+ end
+
+ elements
+ end
end
end
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 19ad110db58..71c974b4c09 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -160,7 +160,7 @@ class Blob < SimpleDelegator
if stored_externally?
if rich_viewer
rich_viewer.binary?
- elsif Linguist::Language.find_by_filename(name).any?
+ elsif Linguist::Language.find_by_extension(name).any?
false
elsif _mime_type
_mime_type.binary?
diff --git a/app/models/chat_name.rb b/app/models/chat_name.rb
index f321db75eeb..fbd0f123341 100644
--- a/app/models/chat_name.rb
+++ b/app/models/chat_name.rb
@@ -1,4 +1,6 @@
class ChatName < ActiveRecord::Base
+ LAST_USED_AT_INTERVAL = 1.hour
+
belongs_to :service
belongs_to :user
@@ -9,4 +11,23 @@ class ChatName < ActiveRecord::Base
validates :user_id, uniqueness: { scope: [:service_id] }
validates :chat_id, uniqueness: { scope: [:service_id, :team_id] }
+
+ # Updates the "last_used_timestamp" but only if it wasn't already updated
+ # recently.
+ #
+ # The throttling this method uses is put in place to ensure that high chat
+ # traffic doesn't result in many UPDATE queries being performed.
+ def update_last_used_at
+ return unless update_last_used_at?
+
+ obtained = Gitlab::ExclusiveLease
+ .new("chat_name/last_used_at/#{id}", timeout: LAST_USED_AT_INTERVAL.to_i)
+ .try_obtain
+
+ touch(:last_used_at) if obtained
+ end
+
+ def update_last_used_at?
+ last_used_at.nil? || last_used_at > LAST_USED_AT_INTERVAL.ago
+ end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 2999e133f97..53a0d787988 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -474,7 +474,7 @@ module Ci
if cache && project.jobs_cache_index
cache = cache.merge(
- key: "#{cache[:key]}_#{project.jobs_cache_index}")
+ key: "#{cache[:key]}-#{project.jobs_cache_index}")
end
[cache]
@@ -550,6 +550,7 @@ module Ci
variables = [
{ key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true },
+ { key: 'GITLAB_FEATURES', value: project.namespace.features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index afeae69ba39..1dd0e050ba9 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -6,7 +6,10 @@ module Ci
belongs_to :group
- validates :key, uniqueness: { scope: :group_id }
+ validates :key, uniqueness: {
+ scope: :group_id,
+ message: "(%{value}) has already been taken"
+ }
scope :unprotected, -> { where(protected: false) }
end
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 2abe90dd181..a72a815bfe8 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -13,7 +13,7 @@ module Ci
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
has_many :stages
- has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id
+ has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id
has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent
has_many :variables, class_name: 'Ci::PipelineVariable'
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 67d3ec81b6f..7c71291de84 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -6,7 +6,10 @@ module Ci
belongs_to :project
- validates :key, uniqueness: { scope: [:project_id, :environment_scope] }
+ validates :key, uniqueness: {
+ scope: [:project_id, :environment_scope],
+ message: "(%{value}) has already been taken"
+ }
scope :unprotected, -> { where(protected: false) }
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index 2d2d89af030..add5fcf0e79 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -116,6 +116,10 @@ class Commit
raw.id
end
+ def project_id
+ project.id
+ end
+
def ==(other)
other.is_a?(self.class) && raw == other.raw
end
@@ -413,6 +417,10 @@ class Commit
!!(title =~ WIP_REGEX)
end
+ def merged_merge_request?(user)
+ !!merged_merge_request(user)
+ end
+
private
def commit_reference(from, referable_commit_id, full: false)
@@ -441,10 +449,6 @@ class Commit
changes
end
- def merged_merge_request?(user)
- !!merged_merge_request(user)
- end
-
def merged_merge_request_no_cache(user)
MergeRequestsFinder.new(user, project_id: project.id).find_by(merge_commit_sha: id) if merge_commit?
end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index fd6703831e4..caf8afa97f9 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -94,6 +94,14 @@ module Milestoneish
Gitlab::TimeTrackingFormatter.output(total_issue_time_spent)
end
+ def total_issue_time_estimate
+ @total_issue_time_estimate ||= issues.sum(:time_estimate)
+ end
+
+ def human_total_issue_time_estimate
+ Gitlab::TimeTrackingFormatter.output(total_issue_time_estimate)
+ end
+
private
def count_issues_by_state(user)
diff --git a/app/models/concerns/protected_ref_access.rb b/app/models/concerns/protected_ref_access.rb
index 80c9f7d4eb4..bfda5b1678b 100644
--- a/app/models/concerns/protected_ref_access.rb
+++ b/app/models/concerns/protected_ref_access.rb
@@ -35,6 +35,7 @@ module ProtectedRefAccess
def check_access(user)
return true if user.admin?
- project.team.max_member_access(user.id) >= access_level
+ user.can?(:push_code, project) &&
+ project.team.max_member_access(user.id) >= access_level
end
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 3aed071dd49..b6cf168d60e 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -1,8 +1,8 @@
class Deployment < ActiveRecord::Base
include InternalId
- belongs_to :project, required: true, validate: true
- belongs_to :environment, required: true, validate: true
+ belongs_to :project, required: true
+ belongs_to :environment, required: true
belongs_to :user
belongs_to :deployable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 2f6eae605ee..f78c21aebe5 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -4,7 +4,7 @@ class Environment < ActiveRecord::Base
NUMBERS = '0'..'9'
SUFFIX_CHARS = LETTERS.to_a + NUMBERS.to_a
- belongs_to :project, required: true, validate: true
+ belongs_to :project, required: true
has_many :deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
diff --git a/app/models/event.rb b/app/models/event.rb
index 8a79100de5a..75538ba196c 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -96,10 +96,6 @@ class Event < ActiveRecord::Base
self.inheritance_column = 'action'
- # "data" will be removed in 10.0 but it may be possible that JOINs happen that
- # include this column, hence we're ignoring it as well.
- ignore_column :data
-
class << self
def model_name
ActiveModel::Name.new(self, nil, 'event')
diff --git a/app/models/external_issue.rb b/app/models/external_issue.rb
index 2aaba2e4c90..282fd7edcb7 100644
--- a/app/models/external_issue.rb
+++ b/app/models/external_issue.rb
@@ -39,7 +39,7 @@ class ExternalIssue
end
def to_reference(_from = nil, full: nil)
- id
+ reference_link_text
end
def reference_link_text(from = nil)
diff --git a/app/models/identity.rb b/app/models/identity.rb
index b3fa7d8176a..2b433e9b988 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -9,6 +9,7 @@ class Identity < ActiveRecord::Base
validates :user_id, uniqueness: { scope: :provider }
before_save :ensure_normalized_extern_uid, if: :extern_uid_changed?
+ after_destroy :clear_user_synced_attributes, if: :user_synced_attributes_metadata_from_provider?
scope :with_provider, ->(provider) { where(provider: provider) }
scope :with_extern_uid, ->(provider, extern_uid) do
@@ -34,4 +35,12 @@ class Identity < ActiveRecord::Base
self.extern_uid = Identity.normalize_uid(self.provider, self.extern_uid)
end
+
+ def user_synced_attributes_metadata_from_provider?
+ user.user_synced_attributes_metadata&.provider == provider
+ end
+
+ def clear_user_synced_attributes
+ user.user_synced_attributes_metadata&.destroy
+ end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 93628b456f2..c81f7e52bb1 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -159,7 +159,18 @@ class Issue < ActiveRecord::Base
object.all_references(current_user, extractor: ext)
end
- ext.merge_requests.sort_by(&:iid)
+ merge_requests = ext.merge_requests.sort_by(&:iid)
+
+ cross_project_filter = -> (merge_requests) do
+ merge_requests.select { |mr| mr.target_project == project }
+ end
+
+ Ability.merge_requests_readable_by_user(
+ merge_requests, current_user,
+ filters: {
+ read_cross_project: cross_project_filter
+ }
+ )
end
# All branches containing the current issue's ID, except for
diff --git a/app/models/member.rb b/app/models/member.rb
index c47145667b5..2d17795e62d 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -314,7 +314,7 @@ class Member < ActiveRecord::Base
end
def notification_setting
- @notification_setting ||= user.notification_settings_for(source)
+ @notification_setting ||= user&.notification_settings_for(source)
end
def notifiable?(type, opts = {})
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index d025062f562..5bec68ce4f6 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -158,10 +158,12 @@ class MergeRequest < ActiveRecord::Base
end
def rebase_in_progress?
- # The source project can be deleted
- return false unless source_project
+ strong_memoize(:rebase_in_progress) do
+ # The source project can be deleted
+ next false unless source_project
- source_project.repository.rebase_in_progress?(id)
+ source_project.repository.rebase_in_progress?(id)
+ end
end
# Use this method whenever you need to make sure the head_pipeline is synced with the
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index d95489ee9f2..db274ea8172 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -243,6 +243,10 @@ class Namespace < ActiveRecord::Base
all_projects.with_storage_feature(:repository).find_each(&:remove_exports)
end
+ def features
+ []
+ end
+
private
def path_or_parent_changed?
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index c351d2012c6..1e0d1f9edcb 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -61,11 +61,8 @@ module Network
@reserved[i] = []
end
- # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37436
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- commits_sort_by_ref.each do |commit|
- place_chain(commit)
- end
+ commits_sort_by_ref.each do |commit|
+ place_chain(commit)
end
# find parent spaces for not overlap lines
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 472b348a545..fd70e920c7e 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -85,6 +85,7 @@ class NotificationRecipient
return false unless user.can?(:receive_notifications)
return true if @skip_read_ability
+ return false if @target && !user.can?(:read_cross_project)
return false if @project && !user.can?(:read_project, @project)
return true unless read_ability
diff --git a/app/models/pages_domain.rb b/app/models/pages_domain.rb
index d8bf54e0c40..588bd50ed77 100644
--- a/app/models/pages_domain.rb
+++ b/app/models/pages_domain.rb
@@ -1,10 +1,14 @@
class PagesDomain < ActiveRecord::Base
+ VERIFICATION_KEY = 'gitlab-pages-verification-code'.freeze
+ VERIFICATION_THRESHOLD = 3.days.freeze
+
belongs_to :project
validates :domain, hostname: { allow_numeric_hostname: true }
validates :domain, uniqueness: { case_sensitive: false }
validates :certificate, certificate: true, allow_nil: true, allow_blank: true
validates :key, certificate_key: true, allow_nil: true, allow_blank: true
+ validates :verification_code, presence: true, allow_blank: false
validate :validate_pages_domain
validate :validate_matching_key, if: ->(domain) { domain.certificate.present? || domain.key.present? }
@@ -16,10 +20,32 @@ class PagesDomain < ActiveRecord::Base
key: Gitlab::Application.secrets.db_key_base,
algorithm: 'aes-256-cbc'
+ after_initialize :set_verification_code
after_create :update_daemon
- after_save :update_daemon
+ after_update :update_daemon, if: :pages_config_changed?
after_destroy :update_daemon
+ scope :enabled, -> { where('enabled_until >= ?', Time.now ) }
+ scope :needs_verification, -> do
+ verified_at = arel_table[:verified_at]
+ enabled_until = arel_table[:enabled_until]
+ threshold = Time.now + VERIFICATION_THRESHOLD
+
+ where(verified_at.eq(nil).or(enabled_until.eq(nil).or(enabled_until.lt(threshold))))
+ end
+
+ def verified?
+ !!verified_at
+ end
+
+ def unverified?
+ !verified?
+ end
+
+ def enabled?
+ !Gitlab::CurrentSettings.pages_domain_verification_enabled? || enabled_until.present?
+ end
+
def to_param
domain
end
@@ -84,12 +110,49 @@ class PagesDomain < ActiveRecord::Base
@certificate_text ||= x509.try(:to_text)
end
+ # Verification codes may be TXT records for domain or verification_domain, to
+ # support the use of CNAME records on domain.
+ def verification_domain
+ return unless domain.present?
+
+ "_#{VERIFICATION_KEY}.#{domain}"
+ end
+
+ def keyed_verification_code
+ return unless verification_code.present?
+
+ "#{VERIFICATION_KEY}=#{verification_code}"
+ end
+
private
+ def set_verification_code
+ return if self.verification_code.present?
+
+ self.verification_code = SecureRandom.hex(16)
+ end
+
def update_daemon
::Projects::UpdatePagesConfigurationService.new(project).execute
end
+ def pages_config_changed?
+ project_id_changed? ||
+ domain_changed? ||
+ certificate_changed? ||
+ key_changed? ||
+ became_enabled? ||
+ became_disabled?
+ end
+
+ def became_enabled?
+ enabled_until.present? && !enabled_until_was.present?
+ end
+
+ def became_disabled?
+ !enabled_until.present? && enabled_until_was.present?
+ end
+
def validate_matching_key
unless has_matching_key?
self.errors.add(:key, "doesn't match the certificate")
diff --git a/app/models/project.rb b/app/models/project.rb
index 445daa8bf34..23144506f63 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -15,6 +15,7 @@ class Project < ActiveRecord::Base
include ValidAttribute
include ProjectFeaturesCompatibility
include SelectForProjectAuthorization
+ include Presentable
include Routable
include GroupDescendant
include Gitlab::SQL::Pattern
@@ -261,7 +262,7 @@ class Project < ActiveRecord::Base
validates :repository_storage,
presence: true,
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
- validates :variables, variable_duplicates: true
+ validates :variables, variable_duplicates: { scope: :environment_scope }
has_many :uploads, as: :model, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
@@ -317,18 +318,42 @@ 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
- authorized = user
- .project_authorizations
- .select(1)
- .where('project_authorizations.project_id = projects.id')
-
- levels = Gitlab::VisibilityLevel.levels_for_user(user)
-
- where('EXISTS (?) OR projects.visibility_level IN (?)', authorized, levels)
+ #
+ # A caller may pass in a block to modify individual parts of
+ # the query, e.g. to apply .with_feature_available_for_user on top of it.
+ # This is useful for performance as we can stick those additional filters
+ # at the bottom of e.g. the UNION.
+ #
+ # Optionally, turning `use_where_in` off leads to returning a
+ # relation using #from instead of #where. This can perform much better
+ # but leads to trouble when used in conjunction with AR's #merge method.
+ def self.public_or_visible_to_user(user = nil, use_where_in: true, &block)
+ # If we don't get a block passed, use identity to avoid if/else repetitions
+ block = ->(part) { part } unless block_given?
+
+ return block.call(public_to_user) unless user
+
+ # If the user is allowed to see all projects,
+ # we can shortcut and just return.
+ return block.call(all) if user.full_private_access?
+
+ authorized = user
+ .project_authorizations
+ .select(1)
+ .where('project_authorizations.project_id = projects.id')
+ authorized_projects = block.call(where('EXISTS (?)', authorized))
+
+ levels = Gitlab::VisibilityLevel.levels_for_user(user)
+ visible_projects = block.call(where(visibility_level: levels))
+
+ # We use a UNION here instead of OR clauses since this results in better
+ # performance.
+ union = Gitlab::SQL::Union.new([authorized_projects.select('projects.id'), visible_projects.select('projects.id')])
+
+ if use_where_in
+ where("projects.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
else
- public_to_user
+ from("(#{union.to_sql}) AS #{table_name}")
end
end
@@ -1013,6 +1038,9 @@ class Project < ActiveRecord::Base
end
def user_can_push_to_empty_repo?(user)
+ return false unless empty_repo?
+ return false unless Ability.allowed?(user, :push_code, self)
+
!ProtectedBranch.default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
end
@@ -1590,8 +1618,11 @@ class Project < ActiveRecord::Base
end
def protected_for?(ref)
- ProtectedBranch.protected?(self, ref) ||
+ if repository.branch_exists?(ref)
+ ProtectedBranch.protected?(self, ref)
+ elsif repository.tag_exists?(ref)
ProtectedTag.protected?(self, ref)
+ end
end
def deployment_variables
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 30eafe31454..436a870b0c4 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -10,6 +10,8 @@ class JiraService < IssueTrackerService
before_update :reset_password
+ alias_method :project_url, :url
+
# This is confusing, but JiraService does not really support these events.
# The values here are required to display correct options in the service
# configuration screen.
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 1bb576ff971..58731451429 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -69,16 +69,16 @@ class PrometheusService < MonitoringService
client.ping
{ success: true, result: 'Checked API endpoint' }
- rescue Gitlab::PrometheusError => err
+ rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err }
end
def environment_metrics(environment)
- with_reactive_cache(Gitlab::Prometheus::Queries::EnvironmentQuery.name, environment.id, &method(:rename_data_to_metrics))
+ with_reactive_cache(Gitlab::Prometheus::Queries::EnvironmentQuery.name, environment.id, &rename_field(:data, :metrics))
end
def deployment_metrics(deployment)
- metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.environment.id, deployment.id, &method(:rename_data_to_metrics))
+ metrics = with_reactive_cache(Gitlab::Prometheus::Queries::DeploymentQuery.name, deployment.environment.id, deployment.id, &rename_field(:data, :metrics))
metrics&.merge(deployment_time: deployment.created_at.to_i) || {}
end
@@ -107,7 +107,7 @@ class PrometheusService < MonitoringService
data: data,
last_update: Time.now.utc
}
- rescue Gitlab::PrometheusError => err
+ rescue Gitlab::PrometheusClient::Error => err
{ success: false, result: err.message }
end
@@ -116,10 +116,10 @@ class PrometheusService < MonitoringService
Gitlab::PrometheusClient.new(RestClient::Resource.new(api_url))
else
cluster = cluster_with_prometheus(environment_id)
- raise Gitlab::PrometheusError, "couldn't find cluster with Prometheus installed" unless cluster
+ raise Gitlab::PrometheusClient::Error, "couldn't find cluster with Prometheus installed" unless cluster
rest_client = client_from_cluster(cluster)
- raise Gitlab::PrometheusError, "couldn't create proxy Prometheus client" unless rest_client
+ raise Gitlab::PrometheusClient::Error, "couldn't create proxy Prometheus client" unless rest_client
Gitlab::PrometheusClient.new(rest_client)
end
@@ -152,9 +152,11 @@ class PrometheusService < MonitoringService
cluster.application_prometheus.proxy_client
end
- def rename_data_to_metrics(metrics)
- metrics[:metrics] = metrics.delete :data
- metrics
+ def rename_field(old_field, new_field)
+ -> (metrics) do
+ metrics[new_field] = metrics.delete(old_field)
+ metrics
+ end
end
def synchronize_service_state!
diff --git a/app/models/project_services/slash_commands_service.rb b/app/models/project_services/slash_commands_service.rb
index eb4da68bb7e..37ea45109ae 100644
--- a/app/models/project_services/slash_commands_service.rb
+++ b/app/models/project_services/slash_commands_service.rb
@@ -30,10 +30,10 @@ class SlashCommandsService < Service
def trigger(params)
return unless valid_token?(params[:token])
- user = find_chat_user(params)
+ chat_user = find_chat_user(params)
- if user
- Gitlab::SlashCommands::Command.new(project, user, params).execute
+ if chat_user&.user
+ Gitlab::SlashCommands::Command.new(project, chat_user, params).execute
else
url = authorize_chat_name_url(params)
Gitlab::SlashCommands::Presenters::Access.new(url).authorize
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 1cf55fd4332..299a3f32a85 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -492,12 +492,8 @@ class Repository
end
def root_ref
- if raw_repository
- raw_repository.root_ref
- else
- # When the repo does not exist we raise this error so no data is cached.
- raise Gitlab::Git::Repository::NoRepository
- end
+ # 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
end
cache_method :root_ref
@@ -593,7 +589,15 @@ class Repository
def license_key
return unless exists?
- Licensee.license(path).try(:key)
+ # The licensee gem creates a Rugged object from the path:
+ # https://github.com/benbalter/licensee/blob/v8.7.0/lib/licensee/projects/git_project.rb
+ begin
+ Licensee.license(path).try(:key)
+ # Normally we would rescue Rugged::Error, but that is banned by lint-rugged
+ # and we need to migrate this endpoint to Gitaly:
+ # https://gitlab.com/gitlab-org/gitaly/issues/1026
+ rescue
+ end
end
cache_method :license_key
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index 7c8716f8c18..a58c208279e 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -74,6 +74,27 @@ class Snippet < ActiveRecord::Base
@link_reference_pattern ||= super("snippets", /(?<snippet>\d+)/)
end
+ # Returns a collection of snippets that are either public or visible to the
+ # logged in user.
+ #
+ # This method does not verify the user actually has the access to the project
+ # the snippet is in, so it should be only used on a relation that's already scoped
+ # for project access
+ def self.public_or_visible_to_user(user = nil)
+ if user
+ authorized = user
+ .project_authorizations
+ .select(1)
+ .where('project_authorizations.project_id = snippets.project_id')
+
+ levels = Gitlab::VisibilityLevel.levels_for_user(user)
+
+ where('EXISTS (?) OR snippets.visibility_level IN (?) or snippets.author_id = (?)', authorized, levels, user.id)
+ else
+ public_to_user
+ end
+ end
+
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{id}"
diff --git a/app/models/tree.rb b/app/models/tree.rb
index c89b8eca9be..4c1856b67a8 100644
--- a/app/models/tree.rb
+++ b/app/models/tree.rb
@@ -9,10 +9,9 @@ class Tree
@repository = repository
@sha = sha
@path = path
- @recursive = recursive
git_repo = @repository.raw_repository
- @entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
+ @entries = Gitlab::Git::Tree.where(git_repo, @sha, @path, recursive)
end
def readme
@@ -58,21 +57,4 @@ class Tree
def sorted_entries
trees + blobs + submodules
end
-
- private
-
- def get_entries(git_repo, sha, path, recursive: false)
- current_path_entries = Gitlab::Git::Tree.where(git_repo, sha, path)
- ordered_entries = []
-
- current_path_entries.each do |entry|
- ordered_entries << entry
-
- if recursive && entry.dir?
- ordered_entries.concat(get_entries(git_repo, sha, entry.path, recursive: true))
- end
- end
-
- ordered_entries
- end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 05c93d3cb17..8610ca27b7f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -59,6 +59,8 @@ class User < ActiveRecord::Base
# Override Devise::Models::Trackable#update_tracked_fields!
# to limit database writes to at most once every hour
def update_tracked_fields!(request)
+ return if Gitlab::Database.read_only?
+
update_tracked_fields(request)
lease = Gitlab::ExclusiveLease.new("user_update_tracked_fields:#{id}", timeout: 1.hour.to_i)
@@ -249,7 +251,7 @@ class User < ActiveRecord::Base
def find_for_database_authentication(warden_conditions)
conditions = warden_conditions.dup
if login = conditions.delete(:login)
- where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase)
+ where(conditions).find_by("lower(username) = :value OR lower(email) = :value", value: login.downcase.strip)
else
find_by(conditions)
end
@@ -325,8 +327,8 @@ class User < ActiveRecord::Base
SQL
where(
- fuzzy_arel_match(:name, query)
- .or(fuzzy_arel_match(:username, query))
+ fuzzy_arel_match(:name, query, lower_exact_match: true)
+ .or(fuzzy_arel_match(:username, query, lower_exact_match: true))
.or(arel_table[:email].eq(query))
).reorder(order % { query: ActiveRecord::Base.connection.quote(query) }, :name)
end
@@ -551,7 +553,7 @@ class User < ActiveRecord::Base
gpg_keys.each(&:update_invalid_gpg_signatures)
end
- # Returns the groups a user has access to
+ # Returns the groups a user has access to, either through a membership or a project authorization
def authorized_groups
union = Gitlab::SQL::Union
.new([groups.select(:id), authorized_projects.select(:namespace_id)])
@@ -559,6 +561,11 @@ class User < ActiveRecord::Base
Group.where("namespaces.id IN (#{union.to_sql})") # rubocop:disable GitlabSecurity/SqlInjection
end
+ # Returns the groups a user is a member of, either directly or through a parent group
+ def membership_groups
+ Gitlab::GroupHierarchy.new(groups).base_and_descendants
+ end
+
# Returns a relation of groups the user has access to, including their parent
# and child groups (recursively).
def all_expanded_groups
diff --git a/app/policies/base_policy.rb b/app/policies/base_policy.rb
index 8fa7b2753c7..603218aa6df 100644
--- a/app/policies/base_policy.rb
+++ b/app/policies/base_policy.rb
@@ -15,4 +15,7 @@ class BasePolicy < DeclarativePolicy::Base
condition(:restricted_public_level, scope: :global) do
Gitlab::CurrentSettings.current_application_settings.restricted_visibility_levels.include?(Gitlab::VisibilityLevel::PUBLIC)
end
+
+ # This is prevented in some cases in `gitlab-ee`
+ rule { default }.enable :read_cross_project
end
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index f0aa16d2ecf..3f6d7d04667 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -3,6 +3,19 @@ class IssuablePolicy < BasePolicy
condition(:locked, scope: :subject, score: 0) { @subject.discussion_locked? }
+ # We aren't checking `:read_issue` or `:read_merge_request` in this case
+ # because it could be possible for a user to see an issuable-iid
+ # (`:read_issue_iid` or `:read_merge_request_iid`) but then wouldn't be allowed
+ # to read the actual issue after a more expensive `:read_issue` check.
+ #
+ # `:read_issue` & `:read_issue_iid` could diverge in gitlab-ee.
+ condition(:visible_to_user, score: 4) do
+ Project.where(id: @subject.project)
+ .public_or_visible_to_user(@user)
+ .with_feature_available_for_user(@subject, @user)
+ .any?
+ end
+
condition(:is_project_member) { @user && @subject.project && @subject.project.team.member?(@user) }
desc "User is the assignee or author"
diff --git a/app/policies/issue_policy.rb b/app/policies/issue_policy.rb
index bd2d417b2a8..ed499511999 100644
--- a/app/policies/issue_policy.rb
+++ b/app/policies/issue_policy.rb
@@ -13,7 +13,10 @@ class IssuePolicy < IssuablePolicy
rule { confidential & ~can_read_confidential }.policy do
prevent :read_issue
+ prevent :read_issue_iid
prevent :update_issue
prevent :admin_issue
end
+
+ rule { can?(:read_issue) | visible_to_user }.enable :read_issue_iid
end
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index bc3afc626fb..e003376d219 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -1,3 +1,3 @@
class MergeRequestPolicy < IssuablePolicy
- # pass
+ rule { can?(:read_merge_request) | visible_to_user }.enable :read_merge_request_iid
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index 1dd8f0a25a9..3b0550b4dd6 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -80,8 +80,9 @@ class ProjectPolicy < BasePolicy
rule { reporter }.enable :reporter_access
rule { developer }.enable :developer_access
rule { master }.enable :master_access
+ rule { owner | admin }.enable :owner_access
- rule { owner | admin }.policy do
+ rule { can?(:owner_access) }.policy do
enable :guest_access
enable :reporter_access
enable :developer_access
@@ -98,11 +99,6 @@ class ProjectPolicy < BasePolicy
enable :remove_pages
end
- rule { owner | reporter }.policy do
- enable :build_download_code
- enable :build_read_container_image
- end
-
rule { can?(:guest_access) }.policy do
enable :read_project
enable :read_board
@@ -119,9 +115,13 @@ class ProjectPolicy < BasePolicy
enable :create_note
enable :upload_file
enable :read_cycle_analytics
- enable :read_project_snippet
end
+ # These abilities are not allowed to admins that are not members of the project,
+ # that's why they are defined separatly.
+ rule { guest & can?(:download_code) }.enable :build_download_code
+ rule { guest & can?(:read_container_image) }.enable :build_read_container_image
+
rule { can?(:reporter_access) }.policy do
enable :download_code
enable :download_wiki_code
@@ -141,12 +141,19 @@ class ProjectPolicy < BasePolicy
enable :read_merge_request
end
+ # We define `:public_user_access` separately because there are cases in gitlab-ee
+ # where we enable or prevent it based on other coditions.
rule { (~anonymous & public_project) | internal_access }.policy do
enable :public_user_access
end
rule { can?(:public_user_access) }.policy do
+ enable :public_access
enable :guest_access
+
+ enable :fork_project
+ enable :build_download_code
+ enable :build_read_container_image
enable :request_access
end
@@ -197,14 +204,6 @@ class ProjectPolicy < BasePolicy
enable :create_cluster
end
- rule { can?(:public_user_access) }.policy do
- enable :public_access
-
- enable :fork_project
- enable :build_download_code
- enable :build_read_container_image
- end
-
rule { archived }.policy do
prevent :create_merge_request
prevent :push_code
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
new file mode 100644
index 00000000000..484ac64580d
--- /dev/null
+++ b/app/presenters/project_presenter.rb
@@ -0,0 +1,338 @@
+class ProjectPresenter < Gitlab::View::Presenter::Delegated
+ include ActionView::Helpers::NumberHelper
+ include ActionView::Helpers::UrlHelper
+ include GitlabRoutingHelper
+ include StorageHelper
+ include TreeHelper
+ include Gitlab::Utils::StrongMemoize
+
+ presents :project
+
+ def statistics_anchors(show_auto_devops_callout:)
+ [
+ files_anchor_data,
+ commits_anchor_data,
+ branches_anchor_data,
+ tags_anchor_data,
+ readme_anchor_data,
+ changelog_anchor_data,
+ license_anchor_data,
+ contribution_guide_anchor_data,
+ gitlab_ci_anchor_data,
+ autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
+ kubernetes_cluster_anchor_data
+ ].compact.select { |item| item.enabled }
+ end
+
+ def statistics_buttons(show_auto_devops_callout:)
+ [
+ changelog_anchor_data,
+ license_anchor_data,
+ contribution_guide_anchor_data,
+ autodevops_anchor_data(show_auto_devops_callout: show_auto_devops_callout),
+ kubernetes_cluster_anchor_data,
+ gitlab_ci_anchor_data,
+ koding_anchor_data
+ ].compact.reject { |item| item.enabled }
+ end
+
+ def empty_repo_statistics_anchors
+ [
+ autodevops_anchor_data,
+ kubernetes_cluster_anchor_data
+ ].compact.select { |item| item.enabled }
+ end
+
+ def empty_repo_statistics_buttons
+ [
+ new_file_anchor_data,
+ readme_anchor_data,
+ license_anchor_data,
+ autodevops_anchor_data,
+ kubernetes_cluster_anchor_data
+ ].compact.reject { |item| item.enabled }
+ end
+
+ def default_view
+ return anonymous_project_view unless current_user
+
+ user_view = current_user.project_view
+
+ if can?(current_user, :download_code, project)
+ user_view
+ elsif user_view == "activity"
+ "activity"
+ elsif can?(current_user, :read_wiki, project)
+ "wiki"
+ elsif feature_available?(:issues, current_user)
+ "projects/issues/issues"
+ else
+ "customize_workflow"
+ end
+ end
+
+ def readme_path
+ filename_path(:readme)
+ end
+
+ def changelog_path
+ filename_path(:changelog)
+ end
+
+ def license_path
+ filename_path(:license_blob)
+ end
+
+ def ci_configuration_path
+ filename_path(:gitlab_ci_yml)
+ end
+
+ def contribution_guide_path
+ if project && contribution_guide = repository.contribution_guide
+ project_blob_path(
+ project,
+ tree_join(project.default_branch,
+ contribution_guide.name)
+ )
+ end
+ end
+
+ def add_license_path
+ add_special_file_path(file_name: 'LICENSE')
+ end
+
+ def add_changelog_path
+ add_special_file_path(file_name: 'CHANGELOG')
+ end
+
+ def add_contribution_guide_path
+ add_special_file_path(file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide')
+ end
+
+ def add_ci_yml_path
+ add_special_file_path(file_name: '.gitlab-ci.yml')
+ end
+
+ def add_readme_path
+ add_special_file_path(file_name: 'README.md')
+ end
+
+ def add_koding_stack_path
+ project_new_blob_path(
+ project,
+ default_branch || 'master',
+ file_name: '.koding.yml',
+ commit_message: "Add Koding stack script",
+ content: <<-CONTENT.strip_heredoc
+ provider:
+ aws:
+ access_key: '${var.aws_access_key}'
+ secret_key: '${var.aws_secret_key}'
+ resource:
+ aws_instance:
+ #{project.path}-vm:
+ instance_type: t2.nano
+ user_data: |-
+
+ # Created by GitLab UI for :>
+
+ echo _KD_NOTIFY_@Installing Base packages...@
+
+ apt-get update -y
+ apt-get install git -y
+
+ echo _KD_NOTIFY_@Cloning #{project.name}...@
+
+ export KODING_USER=${var.koding_user_username}
+ export REPO_URL=#{root_url}${var.koding_queryString_repo}.git
+ export BRANCH=${var.koding_queryString_branch}
+
+ sudo -i -u $KODING_USER git clone $REPO_URL -b $BRANCH
+
+ echo _KD_NOTIFY_@#{project.name} cloned.@
+ CONTENT
+ )
+ end
+
+ def license_short_name
+ license = repository.license
+ license&.nickname || license&.name || 'LICENSE'
+ end
+
+ def can_current_user_push_code?
+ strong_memoize(:can_current_user_push_code) do
+ if empty_repo?
+ can?(current_user, :push_code, project)
+ else
+ can_current_user_push_to_branch?(default_branch)
+ end
+ end
+ end
+
+ def can_current_user_push_to_branch?(branch)
+ return false unless repository.branch_exists?(branch)
+
+ ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch)
+ end
+
+ def files_anchor_data
+ OpenStruct.new(enabled: true,
+ label: _('Files (%{human_size})') % { human_size: storage_counter(statistics.total_repository_size) },
+ link: project_tree_path(project))
+ end
+
+ def commits_anchor_data
+ OpenStruct.new(enabled: true,
+ label: n_('Commit (%{commit_count})', 'Commits (%{commit_count})', statistics.commit_count) % { commit_count: number_with_delimiter(statistics.commit_count) },
+ link: project_commits_path(project, repository.root_ref))
+ end
+
+ def branches_anchor_data
+ OpenStruct.new(enabled: true,
+ label: n_('Branch (%{branch_count})', 'Branches (%{branch_count})', repository.branch_count) % { branch_count: number_with_delimiter(repository.branch_count) },
+ link: project_branches_path(project))
+ end
+
+ def tags_anchor_data
+ OpenStruct.new(enabled: true,
+ label: n_('Tag (%{tag_count})', 'Tags (%{tag_count})', repository.tag_count) % { tag_count: number_with_delimiter(repository.tag_count) },
+ link: project_tags_path(project))
+ end
+
+ def new_file_anchor_data
+ if current_user && can_current_user_push_code?
+ OpenStruct.new(enabled: false,
+ label: _('New file'),
+ link: project_new_blob_path(project, default_branch || 'master'),
+ class_modifier: 'new')
+ end
+ end
+
+ def readme_anchor_data
+ if current_user && can_current_user_push_code? && repository.readme.blank?
+ OpenStruct.new(enabled: false,
+ label: _('Add Readme'),
+ link: add_readme_path)
+ elsif repository.readme.present?
+ OpenStruct.new(enabled: true,
+ label: _('Readme'),
+ link: default_view != 'readme' ? readme_path : '#readme')
+ end
+ end
+
+ def changelog_anchor_data
+ if current_user && can_current_user_push_code? && repository.changelog.blank?
+ OpenStruct.new(enabled: false,
+ label: _('Add Changelog'),
+ link: add_changelog_path)
+ elsif repository.changelog.present?
+ OpenStruct.new(enabled: true,
+ label: _('Changelog'),
+ link: changelog_path)
+ end
+ end
+
+ def license_anchor_data
+ if current_user && can_current_user_push_code? && repository.license_blob.blank?
+ OpenStruct.new(enabled: false,
+ label: _('Add License'),
+ link: add_license_path)
+ elsif repository.license_blob.present?
+ OpenStruct.new(enabled: true,
+ label: license_short_name,
+ link: license_path)
+ end
+ end
+
+ def contribution_guide_anchor_data
+ if current_user && can_current_user_push_code? && repository.contribution_guide.blank?
+ OpenStruct.new(enabled: false,
+ label: _('Add Contribution guide'),
+ link: add_contribution_guide_path)
+ elsif repository.contribution_guide.present?
+ OpenStruct.new(enabled: true,
+ label: _('Contribution guide'),
+ link: contribution_guide_path)
+ end
+ end
+
+ def autodevops_anchor_data(show_auto_devops_callout: false)
+ if current_user && can?(current_user, :admin_pipeline, project) && repository.gitlab_ci_yml.blank? && !show_auto_devops_callout
+ OpenStruct.new(enabled: auto_devops_enabled?,
+ label: auto_devops_enabled? ? _('Auto DevOps enabled') : _('Enable Auto DevOps'),
+ link: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ elsif auto_devops_enabled?
+ OpenStruct.new(enabled: true,
+ label: _('Auto DevOps enabled'),
+ link: nil)
+ end
+ end
+
+ def kubernetes_cluster_anchor_data
+ if current_user && can?(current_user, :create_cluster, project)
+ cluster_link = clusters.count == 1 ? project_cluster_path(project, clusters.first) : project_clusters_path(project)
+
+ if clusters.empty?
+ cluster_link = new_project_cluster_path(project)
+ end
+
+ OpenStruct.new(enabled: !clusters.empty?,
+ label: clusters.empty? ? _('Add Kubernetes cluster') : _('Kubernetes configured'),
+ link: cluster_link)
+ end
+ end
+
+ def gitlab_ci_anchor_data
+ if current_user && can_current_user_push_code? && repository.gitlab_ci_yml.blank? && !auto_devops_enabled?
+ OpenStruct.new(enabled: false,
+ label: _('Set up CI/CD'),
+ link: add_ci_yml_path)
+ elsif repository.gitlab_ci_yml.present?
+ OpenStruct.new(enabled: true,
+ label: _('CI/CD configuration'),
+ link: ci_configuration_path)
+ end
+ end
+
+ def koding_anchor_data
+ if current_user && can_current_user_push_code? && koding_enabled? && repository.koding_yml.blank?
+ OpenStruct.new(enabled: false,
+ label: _('Set up Koding'),
+ link: add_koding_stack_path)
+ end
+ end
+
+ private
+
+ def filename_path(filename)
+ if blob = repository.public_send(filename) # rubocop:disable GitlabSecurity/PublicSend
+ project_blob_path(
+ project,
+ tree_join(default_branch, blob.name)
+ )
+ end
+ end
+
+ def anonymous_project_view
+ if !project.empty_repo? && can?(current_user, :download_code, project)
+ 'files'
+ else
+ 'activity'
+ end
+ end
+
+ def add_special_file_path(file_name:, commit_message: nil, branch_name: nil)
+ commit_message ||= s_("CommitMessage|Add %{file_name}") % { file_name: file_name }
+ project_new_blob_path(
+ project,
+ project.default_branch || 'master',
+ file_name: file_name,
+ commit_message: commit_message,
+ branch_name: branch_name
+ )
+ end
+
+ def koding_enabled?
+ Gitlab::CurrentSettings.koding_enabled?
+ end
+end
diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb
index aca4e4ca488..15ec0f89bb2 100644
--- a/app/serializers/group_child_entity.rb
+++ b/app/serializers/group_child_entity.rb
@@ -11,9 +11,7 @@ class GroupChildEntity < Grape::Entity
end
expose :can_edit do |instance|
- return false unless request.respond_to?(:current_user)
-
- can?(request.current_user, "admin_#{type}", instance)
+ can_edit?
end
expose :edit_path do |instance|
@@ -83,4 +81,17 @@ class GroupChildEntity < Grape::Entity
def markdown_description
markdown_field(object, :description)
end
+
+ def can_edit?
+ return false unless request.respond_to?(:current_user)
+
+ if project?
+ # Avoid checking rights for each project, as it might be expensive if the
+ # user cannot read cross project.
+ can?(request.current_user, :read_cross_project) &&
+ can?(request.current_user, :admin_project, object)
+ else
+ can?(request.current_user, :admin_group, object)
+ end
+ end
end
diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb
new file mode 100644
index 00000000000..74de1e79a8f
--- /dev/null
+++ b/app/serializers/project_serializer.rb
@@ -0,0 +1,3 @@
+class ProjectSerializer < BaseSerializer
+ entity ProjectEntity
+end
diff --git a/app/services/chat_names/find_user_service.rb b/app/services/chat_names/find_user_service.rb
index 4f5c5567b42..d458b814183 100644
--- a/app/services/chat_names/find_user_service.rb
+++ b/app/services/chat_names/find_user_service.rb
@@ -9,8 +9,8 @@ module ChatNames
chat_name = find_chat_name
return unless chat_name
- chat_name.touch(:last_used_at)
- chat_name.user
+ chat_name.update_last_used_at
+ chat_name
end
private
diff --git a/app/services/ci/create_trace_artifact_service.rb b/app/services/ci/create_trace_artifact_service.rb
index 280a2c3afa4..ffde824972c 100644
--- a/app/services/ci/create_trace_artifact_service.rb
+++ b/app/services/ci/create_trace_artifact_service.rb
@@ -4,13 +4,33 @@ module Ci
return if job.job_artifacts_trace
job.trace.read do |stream|
- if stream.file?
- job.create_job_artifacts_trace!(
- project: job.project,
- file_type: :trace,
- file: stream)
+ break unless stream.file?
+
+ clone_file!(stream.path, JobArtifactUploader.workhorse_upload_path) do |clone_path|
+ create_job_trace!(job, clone_path)
+ FileUtils.rm(stream.path)
end
end
end
+
+ private
+
+ def create_job_trace!(job, path)
+ File.open(path) do |stream|
+ job.create_job_artifacts_trace!(
+ project: job.project,
+ file_type: :trace,
+ file: stream)
+ end
+ end
+
+ def clone_file!(src_path, temp_dir)
+ FileUtils.mkdir_p(temp_dir)
+ Dir.mktmpdir('tmp-trace', temp_dir) do |dir_path|
+ temp_path = File.join(dir_path, "job.log")
+ FileUtils.copy(src_path, temp_path)
+ yield(temp_path)
+ end
+ end
end
end
diff --git a/app/services/clusters/gcp/finalize_creation_service.rb b/app/services/clusters/gcp/finalize_creation_service.rb
index cea56f4e849..15ab2d54404 100644
--- a/app/services/clusters/gcp/finalize_creation_service.rb
+++ b/app/services/clusters/gcp/finalize_creation_service.rb
@@ -30,10 +30,10 @@ module Clusters
ca_cert: Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
username: gke_cluster.master_auth.username,
password: gke_cluster.master_auth.password,
- token: request_kuberenetes_token)
+ token: request_kubernetes_token)
end
- def request_kuberenetes_token
+ def request_kubernetes_token
Ci::FetchKubernetesTokenService.new(
'https://' + gke_cluster.endpoint,
Base64.decode64(gke_cluster.master_auth.cluster_ca_certificate),
diff --git a/app/services/delete_merged_branches_service.rb b/app/services/delete_merged_branches_service.rb
index cb235a85daf..c98d1e3c540 100644
--- a/app/services/delete_merged_branches_service.rb
+++ b/app/services/delete_merged_branches_service.rb
@@ -6,18 +6,14 @@ class DeleteMergedBranchesService < BaseService
def execute
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project)
- # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37438
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- branches = project.repository.branch_names
- branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) }
- # Prevent deletion of branches relevant to open merge requests
- branches -= merge_request_branch_names
- # Prevent deletion of protected branches
- branches = branches.reject { |branch| ProtectedBranch.protected?(project, branch) }
+ branches = project.repository.merged_branch_names
+ # Prevent deletion of branches relevant to open merge requests
+ branches -= merge_request_branch_names
+ # Prevent deletion of protected branches
+ branches = branches.reject { |branch| ProtectedBranch.protected?(project, branch) }
- branches.each do |branch|
- DeleteBranchService.new(project, current_user).execute(branch)
- end
+ branches.each do |branch|
+ DeleteBranchService.new(project, current_user).execute(branch)
end
end
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index d6f08fc3cce..5c337a9faa5 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -11,8 +11,8 @@ module Groups
def execute
return nil unless group_path
- if group = Group.find_by_full_path(group_path)
- return group
+ if namespace = namespace_or_group(group_path)
+ return namespace
end
if group_path.include?('/') && !Group.supports_nested_groups?
@@ -40,10 +40,14 @@ module Groups
)
new_params[:visibility_level] ||= Gitlab::CurrentSettings.current_application_settings.default_group_visibility
- last_group = Group.find_by_full_path(partial_path) || Groups::CreateService.new(current_user, new_params).execute
+ last_group = namespace_or_group(partial_path) || Groups::CreateService.new(current_user, new_params).execute
end
last_group
end
+
+ def namespace_or_group(group_path)
+ Namespace.find_by_full_path(group_path)
+ end
end
end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index e7463e6e25c..e87fd49d193 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -77,8 +77,12 @@ class IssuableBaseService < BaseService
return unless labels
params[:label_ids] = labels.split(",").map do |label_name|
- service = Labels::FindOrCreateService.new(current_user, project, title: label_name.strip)
- label = service.execute
+ label = Labels::FindOrCreateService.new(
+ current_user,
+ parent,
+ title: label_name.strip,
+ available_labels: available_labels
+ ).execute
label.try(:id)
end.compact
@@ -102,7 +106,7 @@ class IssuableBaseService < BaseService
end
def available_labels
- LabelsFinder.new(current_user, project_id: @project.id).execute
+ @available_labels ||= LabelsFinder.new(current_user, project_id: @project.id).execute
end
def merge_quick_actions_into_params!(issuable)
@@ -247,7 +251,7 @@ class IssuableBaseService < BaseService
when 'add'
todo_service.mark_todo(issuable, current_user)
when 'done'
- todo = TodosFinder.new(current_user).execute.find_by(target: issuable)
+ todo = TodosFinder.new(current_user).find_by(target: issuable)
todo_service.mark_todos_as_done_by_ids(todo, current_user) if todo
end
end
@@ -303,4 +307,8 @@ class IssuableBaseService < BaseService
def update_project_counter_caches?(issuable)
issuable.state_changed?
end
+
+ def parent
+ project
+ end
end
diff --git a/app/services/issues/fetch_referenced_merge_requests_service.rb b/app/services/issues/fetch_referenced_merge_requests_service.rb
new file mode 100644
index 00000000000..39c8ded9df4
--- /dev/null
+++ b/app/services/issues/fetch_referenced_merge_requests_service.rb
@@ -0,0 +1,12 @@
+module Issues
+ class FetchReferencedMergeRequestsService < Issues::BaseService
+ def execute(issue)
+ referenced_merge_requests = issue.referenced_merge_requests(current_user)
+ referenced_merge_requests = Gitlab::IssuableSorter.sort(project, referenced_merge_requests) { |i| i.iid.to_s }
+ closed_by_merge_requests = issue.closed_by_merge_requests(current_user)
+ closed_by_merge_requests = Gitlab::IssuableSorter.sort(project, closed_by_merge_requests) { |i| i.iid.to_s }
+
+ [referenced_merge_requests, closed_by_merge_requests]
+ end
+ end
+end
diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb
index 2f511ab44b7..7140890d201 100644
--- a/app/services/issues/move_service.rb
+++ b/app/services/issues/move_service.rb
@@ -19,19 +19,10 @@ module Issues
# on rewriting notes (unfolding references)
#
ActiveRecord::Base.transaction do
- # New issue tasks
- #
@new_issue = create_new_issue
- rewrite_notes
- rewrite_issue_award_emoji
- add_note_moved_from
-
- # Old issue tasks
- #
- add_note_moved_to
- close_issue
- mark_as_moved
+ update_new_issue
+ update_old_issue
end
notify_participants
@@ -41,11 +32,24 @@ module Issues
private
+ def update_new_issue
+ rewrite_notes
+ rewrite_issue_award_emoji
+ add_note_moved_from
+ end
+
+ def update_old_issue
+ add_note_moved_to
+ close_issue
+ mark_as_moved
+ end
+
def create_new_issue
new_params = { id: nil, iid: nil, label_ids: cloneable_label_ids,
milestone_id: cloneable_milestone_id,
project: @new_project, author: @old_issue.author,
- description: rewrite_content(@old_issue.description) }
+ description: rewrite_content(@old_issue.description),
+ assignee_ids: @old_issue.assignee_ids }
new_params = @old_issue.serializable_hash.symbolize_keys.merge(new_params)
CreateService.new(@new_project, @current_user, new_params).execute
diff --git a/app/services/labels/find_or_create_service.rb b/app/services/labels/find_or_create_service.rb
index 940c8b333d3..079f611b3f3 100644
--- a/app/services/labels/find_or_create_service.rb
+++ b/app/services/labels/find_or_create_service.rb
@@ -1,8 +1,9 @@
module Labels
class FindOrCreateService
- def initialize(current_user, project, params = {})
+ def initialize(current_user, parent, params = {})
@current_user = current_user
- @project = project
+ @parent = parent
+ @available_labels = params.delete(:available_labels)
@params = params.dup.with_indifferent_access
end
@@ -13,12 +14,13 @@ module Labels
private
- attr_reader :current_user, :project, :params, :skip_authorization
+ attr_reader :current_user, :parent, :params, :skip_authorization
def available_labels
@available_labels ||= LabelsFinder.new(
current_user,
- project_id: project.id
+ "#{parent_type}_id".to_sym => parent.id,
+ only_group_labels: parent_is_group?
).execute(skip_authorization: skip_authorization)
end
@@ -27,8 +29,8 @@ module Labels
def find_or_create_label
new_label = available_labels.find_by(title: title)
- if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, project))
- new_label = Labels::CreateService.new(params).execute(project: project)
+ if new_label.nil? && (skip_authorization || Ability.allowed?(current_user, :admin_label, parent))
+ new_label = Labels::CreateService.new(params).execute(parent_type.to_sym => parent)
end
new_label
@@ -37,5 +39,13 @@ module Labels
def title
params[:title] || params[:name]
end
+
+ def parent_type
+ parent.model_name.param_key
+ end
+
+ def parent_is_group?
+ parent_type == "group"
+ end
end
end
diff --git a/app/services/members/authorized_destroy_service.rb b/app/services/members/authorized_destroy_service.rb
index de3a252d6c6..2e89f00dad8 100644
--- a/app/services/members/authorized_destroy_service.rb
+++ b/app/services/members/authorized_destroy_service.rb
@@ -11,6 +11,7 @@ module Members
Member.transaction do
unassign_issues_and_merge_requests(member) unless member.invite?
+ member.notification_setting&.destroy
member.destroy
end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 2ae855d078b..4b186d93772 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -134,7 +134,7 @@ module MergeRequests
end
def append_closes_description
- return unless issue
+ return unless issue&.to_reference.present?
closes_issue = "Closes #{issue.to_reference}"
@@ -160,10 +160,12 @@ module MergeRequests
merge_request.title = "Resolve \"#{issue.title}\"" if issue.is_a?(Issue)
- unless merge_request.title
+ return if merge_request.title.present?
+
+ if issue_iid.present?
+ merge_request.title = "Resolve #{issue.to_reference}"
branch_title = source_branch.downcase.remove(issue_iid.downcase).titleize.humanize
- merge_request.title = "Resolve #{issue_iid}"
- merge_request.title += " \"#{branch_title}\"" unless branch_title.empty?
+ merge_request.title += " \"#{branch_title}\"" if branch_title.present?
end
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 8c84ccfcc92..e07ecda27b5 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -208,7 +208,12 @@ class NotificationService
def new_access_request(member)
return true unless member.notifiable?(:subscription)
- mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later
+ recipients = member.source.members.owners_and_masters
+ if fallback_to_group_owners_masters?(recipients, member)
+ recipients = member.source.group.members.owners_and_masters
+ end
+
+ recipients.each { |recipient| deliver_access_request_email(recipient, member) }
end
def decline_access_request(member)
@@ -334,6 +339,30 @@ class NotificationService
end
end
+ def pages_domain_verification_succeeded(domain)
+ recipients_for_pages_domain(domain).each do |user|
+ mailer.pages_domain_verification_succeeded_email(domain, user).deliver_later
+ end
+ end
+
+ def pages_domain_verification_failed(domain)
+ recipients_for_pages_domain(domain).each do |user|
+ mailer.pages_domain_verification_failed_email(domain, user).deliver_later
+ end
+ end
+
+ def pages_domain_enabled(domain)
+ recipients_for_pages_domain(domain).each do |user|
+ mailer.pages_domain_enabled_email(domain, user).deliver_later
+ end
+ end
+
+ def pages_domain_disabled(domain)
+ recipients_for_pages_domain(domain).each do |user|
+ mailer.pages_domain_disabled_email(domain, user).deliver_later
+ end
+ end
+
protected
def new_resource_email(target, method)
@@ -428,6 +457,14 @@ class NotificationService
private
+ def recipients_for_pages_domain(domain)
+ project = domain.project
+
+ return [] unless project
+
+ notifiable_users(project.team.masters, :watch, target: project)
+ end
+
def notifiable?(*args)
NotificationRecipientService.notifiable?(*args)
end
@@ -435,4 +472,14 @@ class NotificationService
def notifiable_users(*args)
NotificationRecipientService.notifiable_users(*args)
end
+
+ def deliver_access_request_email(recipient, member)
+ mailer.member_access_requested_email(member.real_source_type, member.id, recipient.user.notification_email).deliver_later
+ end
+
+ def fallback_to_group_owners_masters?(recipients, member)
+ return false if recipients.present?
+
+ member.source.respond_to?(:group) && member.source.group
+ end
end
diff --git a/app/services/projects/autocomplete_service.rb b/app/services/projects/autocomplete_service.rb
index 1ae2c40872a..e61ecb696d0 100644
--- a/app/services/projects/autocomplete_service.rb
+++ b/app/services/projects/autocomplete_service.rb
@@ -50,16 +50,7 @@ module Projects
return [] unless noteable&.is_a?(Issuable)
- opts = {
- project: project,
- issuable: noteable,
- current_user: current_user
- }
- QuickActions::InterpretService.command_definitions.map do |definition|
- next unless definition.available?(opts)
-
- definition.to_h(opts)
- end.compact
+ QuickActions::InterpretService.new(project, current_user).available_commands(noteable)
end
end
end
diff --git a/app/services/projects/create_from_template_service.rb b/app/services/projects/create_from_template_service.rb
index 87d9ed7a0e6..a549cfbabea 100644
--- a/app/services/projects/create_from_template_service.rb
+++ b/app/services/projects/create_from_template_service.rb
@@ -5,11 +5,15 @@ module Projects
end
def execute
- params[:file] = Gitlab::ProjectTemplate.find(params[:template_name]).file
+ template_name = params.delete(:template_name)
+ file = Gitlab::ProjectTemplate.find(template_name).file
+
+ params[:file] = file
+
+ GitlabProjectsImportService.new(current_user, params).execute
- GitlabProjectsImportService.new(@current_user, @params).execute
ensure
- params[:file]&.close
+ file&.close
end
end
end
diff --git a/app/services/projects/gitlab_projects_import_service.rb b/app/services/projects/gitlab_projects_import_service.rb
index a3d7f5cbed5..a68ecb4abe1 100644
--- a/app/services/projects/gitlab_projects_import_service.rb
+++ b/app/services/projects/gitlab_projects_import_service.rb
@@ -11,12 +11,14 @@ module Projects
def execute
FileUtils.mkdir_p(File.dirname(import_upload_path))
+
+ file = params.delete(:file)
FileUtils.copy_entry(file.path, import_upload_path)
- Gitlab::ImportExport::ProjectCreator.new(params[:namespace_id],
- current_user,
- import_upload_path,
- params[:path]).execute
+ params[:import_type] = 'gitlab_project'
+ params[:import_source] = import_upload_path
+
+ ::Projects::CreateService.new(current_user, params).execute
end
private
@@ -28,9 +30,5 @@ module Projects
def tmp_filename
SecureRandom.hex
end
-
- def file
- params[:file]
- end
end
end
diff --git a/app/services/projects/update_pages_configuration_service.rb b/app/services/projects/update_pages_configuration_service.rb
index cacb74b1205..52ff64cc938 100644
--- a/app/services/projects/update_pages_configuration_service.rb
+++ b/app/services/projects/update_pages_configuration_service.rb
@@ -23,7 +23,7 @@ module Projects
end
def pages_domains_config
- project.pages_domains.map do |domain|
+ enabled_pages_domains.map do |domain|
{
domain: domain.domain,
certificate: domain.certificate,
@@ -32,6 +32,14 @@ module Projects
end
end
+ def enabled_pages_domains
+ if Gitlab::CurrentSettings.pages_domain_verification_enabled?
+ project.pages_domains.enabled
+ else
+ project.pages_domains
+ end
+ end
+
def reload_daemon
# GitLab Pages daemon constantly watches for modification time of `pages.path`
# It reloads configuration when `pages.path` is modified
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 669c1ba0a22..1e9bd84e749 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -7,6 +7,18 @@ module QuickActions
SHRUG = '¯\\_(ツ)_/¯'.freeze
TABLEFLIP = '(╯°□°)╯︵ â”»â”â”»'.freeze
+ # Takes an issuable and returns an array of all the available commands
+ # represented with .to_h
+ def available_commands(issuable)
+ @issuable = issuable
+
+ self.class.command_definitions.map do |definition|
+ next unless definition.available?(self)
+
+ definition.to_h(self)
+ end.compact
+ end
+
# Takes a text and interprets the commands that are extracted from it.
# Returns the content without commands, and hash of changes to be applied to a record.
def execute(content, issuable)
@@ -15,8 +27,8 @@ module QuickActions
@issuable = issuable
@updates = {}
- content, commands = extractor.extract_commands(content, context)
- extract_updates(commands, context)
+ content, commands = extractor.extract_commands(content)
+ extract_updates(commands)
[content, @updates]
end
@@ -28,8 +40,8 @@ module QuickActions
@issuable = issuable
- content, commands = extractor.extract_commands(content, context)
- commands = explain_commands(commands, context)
+ content, commands = extractor.extract_commands(content)
+ commands = explain_commands(commands)
[content, commands]
end
@@ -157,11 +169,11 @@ module QuickActions
params '%"milestone"'
condition do
current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
- project.milestones.active.any?
+ find_milestones(project, state: 'active').any?
end
parse_params do |milestone_param|
extract_references(milestone_param, :milestone).first ||
- project.milestones.find_by(title: milestone_param.strip)
+ find_milestones(project, title: milestone_param.strip).first
end
command :milestone do |milestone|
@updates[:milestone_id] = milestone.id if milestone
@@ -544,6 +556,10 @@ module QuickActions
users
end
+ def find_milestones(project, params = {})
+ MilestonesFinder.new(params.merge(project_ids: [project.id], group_ids: [project.group&.id])).execute
+ end
+
def find_labels(labels_param)
extract_references(labels_param, :label) |
LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split).execute
@@ -557,21 +573,21 @@ module QuickActions
find_labels(labels_param).map(&:id)
end
- def explain_commands(commands, opts)
+ def explain_commands(commands)
commands.map do |name, arg|
definition = self.class.definition_by_name(name)
next unless definition
- definition.explain(self, opts, arg)
+ definition.explain(self, arg)
end.compact
end
- def extract_updates(commands, opts)
+ def extract_updates(commands)
commands.each do |name, arg|
definition = self.class.definition_by_name(name)
next unless definition
- definition.execute(self, opts, arg)
+ definition.execute(self, arg)
end
end
@@ -581,14 +597,5 @@ module QuickActions
ext.references(type)
end
-
- def context
- {
- issuable: issuable,
- current_user: current_user,
- project: project,
- params: params
- }
- end
end
end
diff --git a/app/services/verify_pages_domain_service.rb b/app/services/verify_pages_domain_service.rb
new file mode 100644
index 00000000000..86166047302
--- /dev/null
+++ b/app/services/verify_pages_domain_service.rb
@@ -0,0 +1,107 @@
+require 'resolv'
+
+class VerifyPagesDomainService < BaseService
+ # The maximum number of seconds to be spent on each DNS lookup
+ RESOLVER_TIMEOUT_SECONDS = 15
+
+ # How long verification lasts for
+ VERIFICATION_PERIOD = 7.days
+
+ attr_reader :domain
+
+ def initialize(domain)
+ @domain = domain
+ end
+
+ def execute
+ return error("No verification code set for #{domain.domain}") unless domain.verification_code.present?
+
+ if !verification_enabled? || dns_record_present?
+ verify_domain!
+ elsif expired?
+ disable_domain!
+ else
+ unverify_domain!
+ end
+ end
+
+ private
+
+ def verify_domain!
+ was_disabled = !domain.enabled?
+ was_unverified = domain.unverified?
+
+ # Prevent any pre-existing grace period from being truncated
+ reverify = [domain.enabled_until, VERIFICATION_PERIOD.from_now].compact.max
+
+ domain.update!(verified_at: Time.now, enabled_until: reverify)
+
+ if was_disabled
+ notify(:enabled)
+ elsif was_unverified
+ notify(:verification_succeeded)
+ end
+
+ success
+ end
+
+ def unverify_domain!
+ if domain.verified?
+ domain.update!(verified_at: nil)
+ notify(:verification_failed)
+ end
+
+ error("Couldn't verify #{domain.domain}")
+ end
+
+ def disable_domain!
+ domain.update!(verified_at: nil, enabled_until: nil)
+
+ notify(:disabled)
+
+ error("Couldn't verify #{domain.domain}. It is now disabled.")
+ end
+
+ # A domain is only expired until `disable!` has been called
+ def expired?
+ domain.enabled_until && domain.enabled_until < Time.now
+ end
+
+ def dns_record_present?
+ Resolv::DNS.open do |resolver|
+ resolver.timeouts = RESOLVER_TIMEOUT_SECONDS
+
+ check(domain.domain, resolver) || check(domain.verification_domain, resolver)
+ end
+ end
+
+ def check(domain_name, resolver)
+ records = parse(txt_records(domain_name, resolver))
+
+ records.any? do |record|
+ record == domain.keyed_verification_code || record == domain.verification_code
+ end
+ rescue => err
+ log_error("Failed to check TXT records on #{domain_name} for #{domain.domain}: #{err}")
+ false
+ end
+
+ def txt_records(domain_name, resolver)
+ resolver.getresources(domain_name, Resolv::DNS::Resource::IN::TXT)
+ end
+
+ def parse(records)
+ records.flat_map(&:strings).flat_map(&:split)
+ end
+
+ def verification_enabled?
+ Gitlab::CurrentSettings.pages_domain_verification_enabled?
+ end
+
+ def notify(type)
+ return unless verification_enabled?
+
+ Gitlab::AppLogger.info("Pages domain '#{domain.domain}' changed state to '#{type}'")
+ notification_service.public_send("pages_domain_#{type}", domain) # rubocop:disable GitlabSecurity/PublicSend
+ end
+end
diff --git a/app/uploaders/gitlab_uploader.rb b/app/uploaders/gitlab_uploader.rb
index 95565a12f85..f12f0466a1d 100644
--- a/app/uploaders/gitlab_uploader.rb
+++ b/app/uploaders/gitlab_uploader.rb
@@ -61,6 +61,10 @@ class GitlabUploader < CarrierWave::Uploader::Base
super || file&.filename
end
+ def model_valid?
+ !!model
+ end
+
private
# Designed to be overridden by child uploaders that have a dynamic path
diff --git a/app/uploaders/personal_file_uploader.rb b/app/uploaders/personal_file_uploader.rb
index 440972affec..d225002ad2e 100644
--- a/app/uploaders/personal_file_uploader.rb
+++ b/app/uploaders/personal_file_uploader.rb
@@ -20,6 +20,12 @@ class PersonalFileUploader < FileUploader
super
end
+ # model_path_segment does not require a model to be passed, so we can always
+ # generate a path, even when there's no model.
+ def model_valid?
+ true
+ end
+
# Revert-Override
def store_dir
store_dirs[object_store]
diff --git a/app/validators/variable_duplicates_validator.rb b/app/validators/variable_duplicates_validator.rb
index 8a9d8892e9b..72660be6c43 100644
--- a/app/validators/variable_duplicates_validator.rb
+++ b/app/validators/variable_duplicates_validator.rb
@@ -1,13 +1,30 @@
# VariableDuplicatesValidator
#
-# This validtor is designed for especially the following condition
+# This validator is designed for especially the following condition
# - Use `accepts_nested_attributes_for :xxx` in a parent model
# - Use `validates :xxx, uniqueness: { scope: :xxx_id }` in a child model
class VariableDuplicatesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
- duplicates = value.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first)
+ return if record.errors.include?(:"#{attribute}.key")
+
+ if options[:scope]
+ scoped = value.group_by do |variable|
+ Array(options[:scope]).map { |attr| variable.send(attr) } # rubocop:disable GitlabSecurity/PublicSend
+ end
+ scoped.each_value { |scope| validate_duplicates(record, attribute, scope) }
+ else
+ validate_duplicates(record, attribute, value)
+ end
+ end
+
+ private
+
+ def validate_duplicates(record, attribute, values)
+ duplicates = values.reject(&:marked_for_destruction?).group_by(&:key).select { |_, v| v.many? }.map(&:first)
if duplicates.any?
- record.errors.add(attribute, "Duplicate variables: #{duplicates.join(", ")}")
+ error_message = "have duplicate values (#{duplicates.join(", ")})"
+ error_message += " for #{values.first.send(options[:scope])} scope" if options[:scope] # rubocop:disable GitlabSecurity/PublicSend
+ record.errors.add(attribute, error_message)
end
end
end
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 60f12030f98..20527d31870 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -237,6 +237,17 @@
.col-sm-10
= f.number_field :max_pages_size, class: 'form-control'
.help-block 0 for unlimited
+ .form-group
+ .col-sm-offset-2.col-sm-10
+ .checkbox
+ = f.label :pages_domain_verification_enabled do
+ = f.check_box :pages_domain_verification_enabled
+ Require users to prove ownership of custom domains
+ .help-block
+ Domain verification is an essential security measure for public GitLab
+ sites. Users are required to demonstrate they control a domain before
+ it is enabled
+ = link_to icon('question-circle'), help_page_path('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
%fieldset
%legend Continuous Integration and Deployment
diff --git a/app/views/admin/jobs/index.html.haml b/app/views/admin/jobs/index.html.haml
index a01676d82a8..4e3e2f7a475 100644
--- a/app/views/admin/jobs/index.html.haml
+++ b/app/views/admin/jobs/index.html.haml
@@ -7,10 +7,9 @@
- build_path_proc = ->(scope) { admin_jobs_path(scope: scope) }
= render "shared/builds/tabs", build_path_proc: build_path_proc, all_builds: @all_builds, scope: @scope
- .nav-controls
- - if @all_builds.running_or_pending.any?
- #stop-jobs-modal
-
+ - if @all_builds.running_or_pending.any?
+ #stop-jobs-modal
+ .nav-controls
%button#stop-jobs-button.btn.btn-danger{ data: { toggle: 'modal',
target: '#stop-jobs-modal',
url: cancel_all_admin_jobs_path } }
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 1e52646b1cc..abec3607cab 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -35,9 +35,8 @@
method: :put, class: 'btn btn-default',
data: { confirm: _("Are you sure you want to reset registration token?") }
- = render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token,
- type: 'shared' }
+ = render partial: 'ci/runner/how_to_setup_shared_runner',
+ locals: { registration_token: Gitlab::CurrentSettings.runners_registration_token }
.append-bottom-20.clearfix
.pull-left
diff --git a/app/views/admin/users/_user.html.haml b/app/views/admin/users/_user.html.haml
index 3b85586e45b..bbfeceff5b9 100644
--- a/app/views/admin/users/_user.html.haml
+++ b/app/views/admin/users/_user.html.haml
@@ -1,6 +1,6 @@
%li.flex-row
.user-avatar
- = image_tag avatar_icon(user), class: "avatar", alt: ''
+ = image_tag avatar_icon_for_user(user), class: "avatar", alt: ''
.row-main-content
.user-name.row-title.str-truncated-100
= link_to user.name, [:admin, user]
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 101667508a9..ec3be869797 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -10,7 +10,7 @@
= @user.name
%ul.well-list
%li
- = image_tag avatar_icon(@user, 60), class: "avatar s60"
+ = image_tag avatar_icon_for_user(@user, 60), class: "avatar s60"
%li
%span.light Profile page:
%strong
diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml
index e6408f35201..3c0881caa06 100644
--- a/app/views/ci/lints/show.html.haml
+++ b/app/views/ci/lints/show.html.haml
@@ -18,6 +18,8 @@
.col-sm-12
.pull-left.prepend-top-10
= submit_tag('Validate', class: 'btn btn-success submit-yml')
+ .pull-right.prepend-top-10
+ = button_tag('Clear', type: 'button', class: 'btn btn-default clear-yml')
.row.prepend-top-20
.col-sm-12
diff --git a/app/views/ci/runner/_how_to_setup_runner.html.haml b/app/views/ci/runner/_how_to_setup_runner.html.haml
index 8db7727b80c..37fb8fbab26 100644
--- a/app/views/ci/runner/_how_to_setup_runner.html.haml
+++ b/app/views/ci/runner/_how_to_setup_runner.html.haml
@@ -1,16 +1,16 @@
- link = link_to _("GitLab Runner section"), 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'
-.bs-callout.help-callout
- %h4= _("How to setup a #{type} Runner for a new project")
+.append-bottom-10
+ %h4= _("Setup a #{type} Runner manually")
- %ol
- %li
- = _("Install a Runner compatible with GitLab CI")
- = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
- %li
- = _("Specify the following URL during the Runner setup:")
- %code#coordinator_address= root_url(only_path: false)
- %li
- = _("Use the following registration token during setup:")
- %code#registration_token= registration_token
- %li
- = _("Start the Runner!")
+%ol
+ %li
+ = _("Install a Runner compatible with GitLab CI")
+ = (_("(checkout the %{link} for information on how to install it).") % { link: link }).html_safe
+ %li
+ = _("Specify the following URL during the Runner setup:")
+ %code#coordinator_address= root_url(only_path: false)
+ %li
+ = _("Use the following registration token during setup:")
+ %code#registration_token= registration_token
+ %li
+ = _("Start the Runner!")
diff --git a/app/views/ci/runner/_how_to_setup_shared_runner.html.haml b/app/views/ci/runner/_how_to_setup_shared_runner.html.haml
new file mode 100644
index 00000000000..2a190cb9250
--- /dev/null
+++ b/app/views/ci/runner/_how_to_setup_shared_runner.html.haml
@@ -0,0 +1,3 @@
+.bs-callout.help-callout
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: registration_token, type: 'shared' }
diff --git a/app/views/ci/runner/_how_to_setup_specific_runner.html.haml b/app/views/ci/runner/_how_to_setup_specific_runner.html.haml
new file mode 100644
index 00000000000..e765a353fe4
--- /dev/null
+++ b/app/views/ci/runner/_how_to_setup_specific_runner.html.haml
@@ -0,0 +1,26 @@
+.bs-callout.help-callout
+ .append-bottom-10
+ %h4= _('Setup a specific Runner automatically')
+
+ %p
+ - link_to_help_page = link_to(_('Learn more about Kubernetes'),
+ help_page_path('user/project/clusters/index'),
+ target: '_blank',
+ rel: 'noopener noreferrer')
+
+ = _('You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page }
+
+ %ol
+ %li
+ = _('Click the button below to begin the install process by navigating to the Kubernetes page')
+ %li
+ = _('Select an existing Kubernetes cluster or create a new one')
+ %li
+ = _('From the Kubernetes cluster details view, install Runner from the applications list')
+
+ = link_to _('Install Runner on Kubernetes'),
+ project_clusters_path(@project),
+ class: 'btn btn-info'
+ %hr
+ = render partial: 'ci/runner/how_to_setup_runner',
+ locals: { registration_token: registration_token, type: 'specific' }
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index 495a55660cb..15201780451 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -5,7 +5,7 @@
- id = variable&.id
- key = variable&.key
- value = variable&.value
-- is_protected = variable && !only_key_value ? variable.protected : true
+- is_protected = variable && !only_key_value ? variable.protected : false
- id_input_name = "#{form_field}[variables_attributes][][id]"
- destroy_input_name = "#{form_field}[variables_attributes][][_destroy]"
diff --git a/app/views/devise/sessions/two_factor.html.haml b/app/views/devise/sessions/two_factor.html.haml
index a039756c7e2..56ec1b3db0d 100644
--- a/app/views/devise/sessions/two_factor.html.haml
+++ b/app/views/devise/sessions/two_factor.html.haml
@@ -1,6 +1,6 @@
- if inject_u2f_api?
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('u2f')
+ = webpack_bundle_tag('u2f')
%div
= render 'devise/shared/tab_single', tab_title: 'Two-Factor Authentication'
diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml
index 205320ed87c..8b9fa3d6b05 100644
--- a/app/views/discussions/_discussion.html.haml
+++ b/app/views/discussions/_discussion.html.haml
@@ -3,7 +3,7 @@
.timeline-entry-inner
.timeline-icon
= link_to user_path(discussion.author) do
- = image_tag avatar_icon(discussion.author), class: "avatar s40"
+ = image_tag avatar_icon_for_user(discussion.author), class: "avatar s40"
.timeline-content
.discussion.js-toggle-container{ data: { discussion_id: discussion.id } }
.discussion-header
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index a97cbd4d4b3..bf540439c79 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -1,3 +1,5 @@
+- message = local_assigns.fetch(:message)
+
- content_for(:title, 'Access Denied')
%img{ :alt => "GitLab Logo", :src => image_path('logo.svg') }
%h1
@@ -5,5 +7,9 @@
.container
%h3 Access Denied
%hr
- %p You are not allowed to access this page.
- %p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
+ - if message
+ %p
+ = message
+ - else
+ %p You are not allowed to access this page.
+ %p Read more about project permissions #{link_to "here", help_page_path("user/permissions"), class: "vlink"}
diff --git a/app/views/events/_event.atom.builder b/app/views/events/_event.atom.builder
index 38741fe6662..d56234e6c1a 100644
--- a/app/views/events/_event.atom.builder
+++ b/app/views/events/_event.atom.builder
@@ -10,7 +10,7 @@ xml.entry do
# eager-loaded. This allows us to re-use the user object's Email address,
# instead of having to run additional queries to figure out what Email to use
# for the avatar.
- xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author))
+ xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon_for_user(event.author))
xml.author do
xml.username event.author_username
diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml
index 00909982d59..ca3f018c5e6 100644
--- a/app/views/groups/issues.html.haml
+++ b/app/views/groups/issues.html.haml
@@ -1,13 +1,13 @@
- page_title "Issues"
-- group_issues_exists = group_issues(@group).exists?
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@group.name} issues")
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'filtered_search'
-- if group_issues_exists
+- if group_issues_count(state: 'all').zero?
+ = render 'shared/empty_states/issues', project_select_button: true
+- else
.top-area
= render 'shared/issuable/nav', type: :issues
.nav-controls
@@ -20,5 +20,3 @@
= render 'shared/issuable/search_bar', type: :issues
= render 'shared/issues'
-- else
- = render 'shared/empty_states/issues', project_select_button: true
diff --git a/app/views/groups/labels/new.html.haml b/app/views/groups/labels/new.html.haml
index ae240490bbd..538c353cf2d 100644
--- a/app/views/groups/labels/new.html.haml
+++ b/app/views/groups/labels/new.html.haml
@@ -1,6 +1,5 @@
- breadcrumb_title "Labels"
- page_title 'New Label'
-- header_title group_title(@group, 'Labels', group_labels_path(@group))
%h3.page-title
New Label
diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml
index 694292aa7c1..520fe217ae1 100644
--- a/app/views/groups/merge_requests.html.haml
+++ b/app/views/groups/merge_requests.html.haml
@@ -2,9 +2,8 @@
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'filtered_search'
-- if @group_merge_requests.empty?
+- if group_merge_requests_count(state: 'all').zero?
= render 'shared/empty_states/merge_requests', project_select_button: true
- else
.top-area
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 63811ea1c81..bf2725dc328 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -1,5 +1,3 @@
-= webpack_bundle_tag 'docs'
-
%div
- if Gitlab::CurrentSettings.help_page_text.present?
= markdown_field(Gitlab::CurrentSettings.current_application_settings, :help_page_text)
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index d6789baea28..c07c148a12a 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,5 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'help'
- page_title @path.split("/").reverse.map(&:humanize)
.documentation.wiki.prepend-top-default
= markdown @markdown
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 445f0dffbcc..ce09b44fbb2 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -1,7 +1,5 @@
- page_title "UI Development Kit", "Help"
- lorem = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed fermentum nisi sapien, non consequat lectus aliquam ultrices. Suspendisse sodales est euismod nunc condimentum, a consectetur diam ornare."
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('ui_development_kit')
.gitlab-ui-dev-kit
%h1 GitLab UI development kit
@@ -68,7 +66,7 @@
.example
.cover-block
.avatar-holder
- = image_tag avatar_icon('admin@example.com', 90), class: "avatar s90", alt: ''
+ = image_tag avatar_icon_for_email('admin@example.com', 90), class: "avatar s90", alt: ''
.cover-title
John Smith
diff --git a/app/views/import/base/create.js.haml b/app/views/import/base/create.js.haml
deleted file mode 100644
index 4dc3a4a0acf..00000000000
--- a/app/views/import/base/create.js.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-- if @project.persisted?
- :plain
- job = $("tr#repo_#{@repo_id}")
- job.attr("id", "project_#{@project.id}")
- target_field = job.find(".import-target")
- target_field.empty()
- target_field.append('#{link_to @project.full_path, project_path(@project)}')
- $("table.import-jobs tbody").prepend(job)
- job.addClass("active").find(".import-actions").html("<i class='fa fa-spinner fa-spin'></i> started")
-- else
- :plain
- job = $("tr#repo_#{@repo_id}")
- job.find(".import-actions").html("<i class='fa fa-exclamation-circle'></i> Error saving project: #{escape_javascript(h(@project.errors.full_messages.join(',')))}")
diff --git a/app/views/import/base/unauthorized.js.haml b/app/views/import/base/unauthorized.js.haml
deleted file mode 100644
index ada5f99f4e2..00000000000
--- a/app/views/import/base/unauthorized.js.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-:plain
- tr = $("tr#repo_#{@repo_id}")
- target_field = tr.find(".import-target")
- import_button = tr.find(".btn-import")
- origin_target = target_field.text()
- project_name = "#{@project_name}"
- origin_namespace = "#{@target_namespace.full_path}"
- target_field.empty()
- target_field.append("<p class='alert alert-danger'>This namespace has already been taken! Please choose another one.</p>")
- target_field.append("<input type='text' name='target_namespace' />")
- target_field.append("/" + project_name)
- target_field.data("project_name", project_name)
- target_field.find('input').prop("value", origin_namespace)
- import_button.enable().removeClass('is-loading')
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 5d68e1e2156..df5841d1911 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -1,7 +1,5 @@
- page_title "GitLab Import"
- header_title "Projects", root_path
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'project_import_gl'
%h3.page-title
= icon('gitlab')
diff --git a/app/views/issues/_issue.atom.builder b/app/views/issues/_issue.atom.builder
index 0c113c08526..21cf6d0dd65 100644
--- a/app/views/issues/_issue.atom.builder
+++ b/app/views/issues/_issue.atom.builder
@@ -3,7 +3,7 @@ xml.entry do
xml.link href: project_issue_url(issue.project, issue)
xml.title truncate(issue.title, length: 80)
xml.updated issue.updated_at.xmlschema
- xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
+ xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon_for_user(issue.author))
xml.author do
xml.name issue.author_name
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index e7fc83a8d04..e6238c0dddb 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -20,32 +20,37 @@
%ul.nav.navbar-nav
- if current_user
= render 'layouts/header/new_dropdown'
- %li.hidden-sm.hidden-xs
- = render 'layouts/search' unless current_controller?(:search)
- %li.visible-sm-inline-block.visible-xs-inline-block
- = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
- = sprite_icon('search', size: 16)
- - if current_user
+ - if header_link?(:search)
+ %li.hidden-sm.hidden-xs
+ = render 'layouts/search' unless current_controller?(:search)
+ %li.visible-sm-inline-block.visible-xs-inline-block
+ = link_to search_path, title: 'Search', aria: { label: "Search" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = sprite_icon('search', size: 16)
+
+ - if header_link?(:issues)
= nav_link(path: 'dashboard#issues', html_options: { class: "user-counter" }) do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('issues', size: 16)
- issues_count = assigned_issuables_count(:issues)
%span.badge.issues-count{ class: ('hidden' if issues_count.zero?) }
= number_with_delimiter(issues_count)
+ - if header_link?(:merge_requests)
= nav_link(path: 'dashboard#merge_requests', html_options: { class: "user-counter" }) do
= link_to assigned_mrs_dashboard_path, title: 'Merge requests', class: 'dashboard-shortcuts-merge_requests', aria: { label: "Merge requests" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('git-merge', size: 16)
- merge_requests_count = assigned_issuables_count(:merge_requests)
%span.badge.merge-requests-count{ class: ('hidden' if merge_requests_count.zero?) }
= number_with_delimiter(merge_requests_count)
+ - if header_link?(:todos)
= nav_link(controller: 'dashboard/todos', html_options: { class: "user-counter" }) do
= link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= sprite_icon('todo-done', size: 16)
%span.badge.todos-count{ class: ('hidden' if todos_pending_count.zero?) }
= todos_count_format(todos_pending_count)
+ - if header_link?(:user_dropdown)
%li.header-user.dropdown
= link_to current_user, class: user_dropdown_class, data: { toggle: "dropdown" } do
- = image_tag avatar_icon(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar"
+ = image_tag avatar_icon_for_user(current_user, 23), width: 23, height: 23, class: "header-user-avatar qa-user-avatar"
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu-nav.dropdown-menu-align-right
%ul
@@ -64,11 +69,11 @@
%li.divider
%li
= link_to "Sign out", destroy_user_session_path, class: "sign-out-link"
- - if session[:impersonator_id]
- %li.impersonation
- = link_to admin_impersonation_path, class: 'impersonation-btn', method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
- = icon('user-secret')
- - else
+ - if header_link?(:admin_impersonation)
+ %li.impersonation
+ = link_to admin_impersonation_path, class: 'impersonation-btn', method: :delete, title: "Stop impersonation", aria: { label: 'Stop impersonation' }, data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
+ = icon('user-secret')
+ - if header_link?(:sign_in)
%li
%div
= link_to "Sign in / Register", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index 74532eba298..f773bd0832d 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -1,53 +1,64 @@
%ul.list-unstyled.navbar-sub-nav
- = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown" }) do
- %a{ href: "#", data: { toggle: "dropdown" } }
- Projects
- = sprite_icon('angle-down', css_class: 'caret-down')
- .dropdown-menu.projects-dropdown-menu
- = render "layouts/nav/projects_dropdown/show"
+ - if dashboard_nav_link?(:projects)
+ = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: { id: 'nav-projects-dropdown', class: "home dropdown header-projects qa-projects-dropdown" }) do
+ %a{ href: "#", data: { toggle: "dropdown" } }
+ Projects
+ = sprite_icon('angle-down', css_class: 'caret-down')
+ .dropdown-menu.projects-dropdown-menu
+ = render "layouts/nav/projects_dropdown/show"
- = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "hidden-xs" }) do
- = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups qa-groups-link', title: 'Groups' do
- Groups
+ - if dashboard_nav_link?(:groups)
+ = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "hidden-xs" }) do
+ = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups qa-groups-link', title: 'Groups' do
+ Groups
- = nav_link(path: 'dashboard#activity', html_options: { class: "visible-lg" }) do
- = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
- Activity
+ - if dashboard_nav_link?(:activity)
+ = nav_link(path: 'dashboard#activity', html_options: { class: "visible-lg" }) do
+ = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
+ Activity
- = nav_link(controller: 'dashboard/milestones', html_options: { class: "visible-lg" }) do
- = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
- Milestones
+ - if dashboard_nav_link?(:milestones)
+ = nav_link(controller: 'dashboard/milestones', html_options: { class: "visible-lg" }) do
+ = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
+ Milestones
- = nav_link(controller: 'dashboard/snippets', html_options: { class: "visible-lg" }) do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
- Snippets
+ - if dashboard_nav_link?(:snippets)
+ = nav_link(controller: 'dashboard/snippets', html_options: { class: "visible-lg" }) do
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
+ Snippets
- %li.header-more.dropdown.hidden-lg
- %a{ href: "#", data: { toggle: "dropdown" } }
- More
- = sprite_icon('angle-down', css_class: 'caret-down')
- .dropdown-menu
- %ul
- = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "visible-xs" }) do
- = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
- Groups
+ - if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets])
+ %li.header-more.dropdown.hidden-lg
+ %a{ href: "#", data: { toggle: "dropdown" } }
+ More
+ = sprite_icon('angle-down', css_class: 'caret-down')
+ .dropdown-menu
+ %ul
+ - if dashboard_nav_link?(:groups)
+ = nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { class: "visible-xs" }) do
+ = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', title: 'Groups' do
+ Groups
- = nav_link(path: 'dashboard#activity') do
- = link_to activity_dashboard_path, title: 'Activity' do
- Activity
+ - if dashboard_nav_link?(:activity)
+ = nav_link(path: 'dashboard#activity') do
+ = link_to activity_dashboard_path, title: 'Activity' do
+ Activity
- = nav_link(controller: 'dashboard/milestones') do
- = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
- Milestones
+ - if dashboard_nav_link?(:milestones)
+ = nav_link(controller: 'dashboard/milestones') do
+ = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', title: 'Milestones' do
+ Milestones
- = nav_link(controller: 'dashboard/snippets') do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
- Snippets
+ - if dashboard_nav_link?(:snippets)
+ = nav_link(controller: 'dashboard/snippets') do
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: 'Snippets' do
+ Snippets
-# Shortcut to Dashboard > Projects
- %li.hidden
- = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
- Projects
+ - if dashboard_nav_link?(:projects)
+ %li.hidden
+ = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
+ Projects
- if current_controller?('ide')
%li.line-separator.hidden-xs
diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml
index cd1c39f3226..50bde9d1754 100644
--- a/app/views/layouts/nav/_explore.html.haml
+++ b/app/views/layouts/nav/_explore.html.haml
@@ -1,12 +1,15 @@
%ul.list-unstyled.navbar-sub-nav
- = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
- = link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
- Projects
- = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
- = link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do
- Groups
- = nav_link(controller: :snippets) do
- = link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do
- Snippets
+ - if explore_nav_link?(:projects)
+ = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do
+ = link_to explore_root_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
+ Projects
+ - if explore_nav_link?(:groups)
+ = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
+ = link_to explore_groups_path, title: 'Groups', class: 'dashboard-shortcuts-groups' do
+ Groups
+ - if explore_nav_link?(:snippets)
+ = nav_link(controller: :snippets) do
+ = link_to explore_snippets_path, title: 'Snippets', class: 'dashboard-shortcuts-snippets' do
+ Snippets
%li
= link_to "Help", help_path, title: 'About GitLab CE'
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 09a43a2cac5..b520f28123f 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -1,5 +1,6 @@
-- issues_count = IssuesFinder.new(current_user, group_id: @group.id, state: 'opened').execute.count
-- merge_requests_count = MergeRequestsFinder.new(current_user, group_id: @group.id, state: 'opened', non_archived: true).execute.count
+- issues_count = group_issues_count(state: 'opened')
+- merge_requests_count = group_merge_requests_count(state: 'opened')
+- issues_sub_menu_items = ['groups#issues', 'labels#index', 'milestones#index']
.nav-sidebar{ class: ("sidebar-collapsed-desktop" if collapsed_sidebar?) }
.nav-sidebar-inner-scroll
@@ -10,84 +11,93 @@
.sidebar-context-title
= @group.name
%ul.sidebar-top-level-items
- = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: 'home' }) do
- = link_to group_path(@group) do
- .nav-icon-container
- = sprite_icon('project')
- %span.nav-item-name
- Overview
+ - if group_sidebar_link?(:overview)
+ = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups', 'analytics#show'], html_options: { class: 'home' }) do
+ = link_to group_path(@group) do
+ .nav-icon-container
+ = sprite_icon('project')
+ %span.nav-item-name
+ Overview
+
+ %ul.sidebar-sub-level-items
+ = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
+ = link_to group_path(@group) do
+ %strong.fly-out-top-item-name
+ #{ _('Overview') }
+ %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
+ %span
+ Details
+
+ - if group_sidebar_link?(:activity)
+ = nav_link(path: 'groups#activity') do
+ = link_to activity_group_path(@group), title: 'Activity' do
+ %span
+ Activity
- %ul.sidebar-sub-level-items
- = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
- = link_to group_path(@group) do
- %strong.fly-out-top-item-name
- #{ _('Overview') }
- %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
- %span
- Details
+ - if group_sidebar_link?(:issues)
+ = nav_link(path: issues_sub_menu_items) do
+ = link_to issues_group_path(@group) do
+ .nav-icon-container
+ = sprite_icon('issues')
+ %span.nav-item-name
+ Issues
+ %span.badge.count= number_with_delimiter(issues_count)
- = nav_link(path: 'groups#activity') do
- = link_to activity_group_path(@group), title: 'Activity' do
- %span
- Activity
+ %ul.sidebar-sub-level-items
+ = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
+ = link_to issues_group_path(@group) do
+ %strong.fly-out-top-item-name
+ #{ _('Issues') }
+ %span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count)
+ %li.divider.fly-out-top-item
+ = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
+ = link_to issues_group_path(@group), title: 'List' do
+ %span
+ List
- = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index']) do
- = link_to issues_group_path(@group) do
- .nav-icon-container
- = sprite_icon('issues')
- %span.nav-item-name
- Issues
- %span.badge.count= number_with_delimiter(issues_count)
+ - if group_sidebar_link?(:labels)
+ = nav_link(path: 'labels#index') do
+ = link_to group_labels_path(@group), title: 'Labels' do
+ %span
+ Labels
- %ul.sidebar-sub-level-items
- = nav_link(path: ['groups#issues', 'labels#index', 'milestones#index'], html_options: { class: "fly-out-top-item" } ) do
- = link_to issues_group_path(@group) do
- %strong.fly-out-top-item-name
- #{ _('Issues') }
- %span.badge.count.issue_counter.fly-out-badge= number_with_delimiter(issues_count)
- %li.divider.fly-out-top-item
- = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do
- = link_to issues_group_path(@group), title: 'List' do
- %span
- List
+ - if group_sidebar_link?(:milestones)
+ = nav_link(path: 'milestones#index') do
+ = link_to group_milestones_path(@group), title: 'Milestones' do
+ %span
+ Milestones
- = nav_link(path: 'labels#index') do
- = link_to group_labels_path(@group), title: 'Labels' do
- %span
- Labels
+ - if group_sidebar_link?(:merge_requests)
+ = nav_link(path: 'groups#merge_requests') do
+ = link_to merge_requests_group_path(@group) do
+ .nav-icon-container
+ = sprite_icon('git-merge')
+ %span.nav-item-name
+ Merge Requests
+ %span.badge.count= number_with_delimiter(merge_requests_count)
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
+ = link_to merge_requests_group_path(@group) do
+ %strong.fly-out-top-item-name
+ #{ _('Merge Requests') }
+ %span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count)
- = nav_link(path: 'milestones#index') do
- = link_to group_milestones_path(@group), title: 'Milestones' do
- %span
- Milestones
+ - if group_sidebar_link?(:group_members)
+ = nav_link(path: 'group_members#index') do
+ = link_to group_group_members_path(@group) do
+ .nav-icon-container
+ = sprite_icon('users')
+ %span.nav-item-name
+ Members
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
+ = link_to group_group_members_path(@group) do
+ %strong.fly-out-top-item-name
+ #{ _('Members') }
- = nav_link(path: 'groups#merge_requests') do
- = link_to merge_requests_group_path(@group) do
- .nav-icon-container
- = sprite_icon('git-merge')
- %span.nav-item-name
- Merge Requests
- %span.badge.count= number_with_delimiter(merge_requests_count)
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(path: 'groups#merge_requests', html_options: { class: "fly-out-top-item" } ) do
- = link_to merge_requests_group_path(@group) do
- %strong.fly-out-top-item-name
- #{ _('Merge Requests') }
- %span.badge.count.merge_counter.js-merge-counter.fly-out-badge= number_with_delimiter(merge_requests_count)
- = nav_link(path: 'group_members#index') do
- = link_to group_group_members_path(@group) do
- .nav-icon-container
- = sprite_icon('users')
- %span.nav-item-name
- Members
- %ul.sidebar-sub-level-items.is-fly-out-only
- = nav_link(path: 'group_members#index', html_options: { class: "fly-out-top-item" } ) do
- = link_to group_group_members_path(@group) do
- %strong.fly-out-top-item-name
- #{ _('Members') }
- - if current_user && can?(current_user, :admin_group, @group)
+ - if group_sidebar_link?(:settings)
= nav_link(path: group_nav_link_paths) do
= link_to edit_group_path(@group) do
.nav-icon-container
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index 2b98cb9de99..059571f795f 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -127,6 +127,19 @@
= link_to project_milestones_path(@project), title: 'Milestones' do
%span
Milestones
+ - if project_nav_tab? :external_issue_tracker
+ = nav_link do
+ - issue_tracker = @project.external_issue_tracker
+ = link_to issue_tracker.issue_tracker_path, class: 'shortcuts-external_tracker' do
+ .nav-icon-container
+ = sprite_icon('issue-external')
+ %span.nav-item-name
+ = issue_tracker.title
+ %ul.sidebar-sub-level-items.is-fly-out-only
+ = nav_link(html_options: { class: "fly-out-top-item" } ) do
+ = link_to issue_tracker.issue_tracker_path do
+ %strong.fly-out-top-item-name
+ = issue_tracker.title
- if project_nav_tab? :merge_requests
= nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :labels, :milestones]) do
diff --git a/app/views/notify/pages_domain_disabled_email.html.haml b/app/views/notify/pages_domain_disabled_email.html.haml
new file mode 100644
index 00000000000..34ce4238a12
--- /dev/null
+++ b/app/views/notify/pages_domain_disabled_email.html.haml
@@ -0,0 +1,15 @@
+%p
+ Following a verification check, your GitLab Pages custom domain has been
+ %strong disabled.
+ This means that your content is no longer visible at #{link_to @domain.url, @domain.url}
+%p
+ Project: #{link_to @project.human_name, project_url(@project)}
+%p
+ Domain: #{link_to @domain.domain, project_pages_domain_url(@project, @domain)}
+%p
+ If this domain has been disabled in error, please follow
+ = link_to 'these instructions', help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ to verify and re-enable your domain.
+%p
+ If you no longer wish to use this domain with GitLab Pages, please remove it
+ from your GitLab project and delete any related DNS records.
diff --git a/app/views/notify/pages_domain_disabled_email.text.haml b/app/views/notify/pages_domain_disabled_email.text.haml
new file mode 100644
index 00000000000..4e81b054b1f
--- /dev/null
+++ b/app/views/notify/pages_domain_disabled_email.text.haml
@@ -0,0 +1,13 @@
+Following a verification check, your GitLab Pages custom domain has been
+**disabled**. This means that your content is no longer visible at #{@domain.url}
+
+Project: #{@project.human_name} (#{project_url(@project)})
+Domain: #{@domain.domain} (#{project_pages_domain_url(@project, @domain)})
+
+If this domain has been disabled in error, please follow these instructions
+to verify and re-enable your domain:
+
+= help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+
+If you no longer wish to use this domain with GitLab Pages, please remove it
+from your GitLab project and delete any related DNS records.
diff --git a/app/views/notify/pages_domain_enabled_email.html.haml b/app/views/notify/pages_domain_enabled_email.html.haml
new file mode 100644
index 00000000000..db09e503f65
--- /dev/null
+++ b/app/views/notify/pages_domain_enabled_email.html.haml
@@ -0,0 +1,11 @@
+%p
+ Following a verification check, your GitLab Pages custom domain has been
+ enabled. You should now be able to view your content at #{link_to @domain.url, @domain.url}
+%p
+ Project: #{link_to @project.human_name, project_url(@project)}
+%p
+ Domain: #{link_to @domain.domain, project_pages_domain_url(@project, @domain)}
+%p
+ Please visit
+ = link_to 'these instructions', help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ for more information about custom domain verification.
diff --git a/app/views/notify/pages_domain_enabled_email.text.haml b/app/views/notify/pages_domain_enabled_email.text.haml
new file mode 100644
index 00000000000..1ed1dbb8315
--- /dev/null
+++ b/app/views/notify/pages_domain_enabled_email.text.haml
@@ -0,0 +1,9 @@
+Following a verification check, your GitLab Pages custom domain has been
+enabled. You should now be able to view your content at #{@domain.url}
+
+Project: #{@project.human_name} (#{project_url(@project)})
+Domain: #{@domain.domain} (#{project_pages_domain_url(@project, @domain)})
+
+Please visit
+= help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+for more information about custom domain verification.
diff --git a/app/views/notify/pages_domain_verification_failed_email.html.haml b/app/views/notify/pages_domain_verification_failed_email.html.haml
new file mode 100644
index 00000000000..0bb0eb09fd5
--- /dev/null
+++ b/app/views/notify/pages_domain_verification_failed_email.html.haml
@@ -0,0 +1,17 @@
+%p
+ Verification has failed for one of your GitLab Pages custom domains!
+%p
+ Project: #{link_to @project.human_name, project_url(@project)}
+%p
+ Domain: #{link_to @domain.domain, project_pages_domain_url(@project, @domain)}
+%p
+ Unless you take action, it will be disabled on
+ %strong= @domain.enabled_until.strftime('%F %T.')
+ Until then, you can view your content at #{link_to @domain.url, @domain.url}
+%p
+ Please visit
+ = link_to 'these instructions', help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ for more information about custom domain verification.
+%p
+ If you no longer wish to use this domain with GitLab Pages, please remove it
+ from your GitLab project and delete any related DNS records.
diff --git a/app/views/notify/pages_domain_verification_failed_email.text.haml b/app/views/notify/pages_domain_verification_failed_email.text.haml
new file mode 100644
index 00000000000..c14e0e0c24d
--- /dev/null
+++ b/app/views/notify/pages_domain_verification_failed_email.text.haml
@@ -0,0 +1,14 @@
+Verification has failed for one of your GitLab Pages custom domains!
+
+Project: #{@project.human_name} (#{project_url(@project)})
+Domain: #{@domain.domain} (#{project_pages_domain_url(@project, @domain)})
+
+Unless you take action, it will be disabled on *#{@domain.enabled_until.strftime('%F %T')}*.
+Until then, you can view your content at #{@domain.url}
+
+Please visit
+= help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+for more information about custom domain verification.
+
+If you no longer wish to use this domain with GitLab Pages, please remove it
+from your GitLab project and delete any related DNS records.
diff --git a/app/views/notify/pages_domain_verification_succeeded_email.html.haml b/app/views/notify/pages_domain_verification_succeeded_email.html.haml
new file mode 100644
index 00000000000..2ead3187b10
--- /dev/null
+++ b/app/views/notify/pages_domain_verification_succeeded_email.html.haml
@@ -0,0 +1,13 @@
+%p
+ One of your GitLab Pages custom domains has been successfully verified!
+%p
+ Project: #{link_to @project.human_name, project_url(@project)}
+%p
+ Domain: #{link_to @domain.domain, project_pages_domain_url(@project, @domain)}
+%p
+ This is a notification. No action is required on your part. You can view your
+ content at #{link_to @domain.url, @domain.url}
+%p
+ Please visit
+ = link_to 'these instructions', help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ for more information about custom domain verification.
diff --git a/app/views/notify/pages_domain_verification_succeeded_email.text.haml b/app/views/notify/pages_domain_verification_succeeded_email.text.haml
new file mode 100644
index 00000000000..e7cdbdee420
--- /dev/null
+++ b/app/views/notify/pages_domain_verification_succeeded_email.text.haml
@@ -0,0 +1,10 @@
+One of your GitLab Pages custom domains has been successfully verified!
+
+Project: #{@project.human_name} (#{project_url(@project)})
+Domain: #{@domain.domain} (#{project_pages_domain_url(@project, @domain)})
+
+No action is required on your part. You can view your content at #{@domain.url}
+
+Please visit
+= help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+for more information about custom domain verification.
diff --git a/app/views/notify/pipeline_failed_email.html.haml b/app/views/notify/pipeline_failed_email.html.haml
index 8eb3f2d5192..38dab104eb5 100644
--- a/app/views/notify/pipeline_failed_email.html.haml
+++ b/app/views/notify/pipeline_failed_email.html.haml
@@ -60,7 +60,7 @@
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %img.avatar{ height: "24", src: avatar_icon_for(commit.author, commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.author
%a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
@@ -76,7 +76,7 @@
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %img.avatar{ height: "24", src: avatar_icon_for(commit.committer, commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.committer
%a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
@@ -100,7 +100,7 @@
triggered by
- if @pipeline.user
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
- %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
%a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
= @pipeline.user.name
diff --git a/app/views/notify/pipeline_success_email.html.haml b/app/views/notify/pipeline_success_email.html.haml
index bae37292d62..7b06e8afa0b 100644
--- a/app/views/notify/pipeline_success_email.html.haml
+++ b/app/views/notify/pipeline_success_email.html.haml
@@ -60,7 +60,7 @@
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon(commit.author || commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %img.avatar{ height: "24", src: avatar_icon_for(commit.author, commit.author_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.author
%a.muted{ href: user_url(commit.author), style: "color:#333333;text-decoration:none;" }
@@ -76,7 +76,7 @@
%tbody
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;" }
- %img.avatar{ height: "24", src: avatar_icon(commit.committer || commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %img.avatar{ height: "24", src: avatar_icon_for(commit.committer, commit.committer_email, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;" }
- if commit.committer
%a.muted{ href: user_url(commit.committer), style: "color:#333333;text-decoration:none;" }
@@ -100,7 +100,7 @@
triggered by
- if @pipeline.user
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;line-height:1.4;vertical-align:middle;padding-right:5px;padding-left:5px", width: "24" }
- %img.avatar{ height: "24", src: avatar_icon(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
+ %img.avatar{ height: "24", src: avatar_icon_for_user(@pipeline.user, 24, only_path: false), style: "display:block;border-radius:12px;margin:-2px 0;", width: "24", alt: "" }/
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:15px;font-weight:500;line-height:1.4;vertical-align:baseline;" }
%a.muted{ href: user_url(@pipeline.user), style: "color:#333333;text-decoration:none;" }
= @pipeline.user.name
diff --git a/app/views/profiles/_head.html.haml b/app/views/profiles/_head.html.haml
index 83ae9129807..a8eb66ca13c 100644
--- a/app/views/profiles/_head.html.haml
+++ b/app/views/profiles/_head.html.haml
@@ -1,2 +1,2 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('profile')
+ = webpack_bundle_tag('profile')
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 79e197ad08b..0f849f6f8b7 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -102,6 +102,3 @@
%p
= s_("Profiles|You don't have access to delete this user.")
.append-bottom-default
-
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('account')
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 5c76d2d8f51..110736dc557 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -20,8 +20,8 @@
or change it at #{link_to Gitlab.config.gravatar.host, 'https://' + Gitlab.config.gravatar.host}
.col-lg-8
.clearfix.avatar-image.append-bottom-default
- = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
- = image_tag avatar_icon(@user, 160), alt: '', class: 'avatar s160'
+ = link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
+ = image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160'
%h5.prepend-top-0= _("Upload new avatar")
.prepend-top-5.append-bottom-10
%button.btn.js-choose-user-avatar-button{ type: 'button' }= _("Choose file...")
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 5207dac3ac2..e58cd20402c 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -6,8 +6,8 @@
- content_for :page_specific_javascripts do
- if inject_u2f_api?
- = page_specific_javascript_bundle_tag('u2f')
- = page_specific_javascript_bundle_tag('two_factor_auth')
+ = webpack_bundle_tag('u2f')
+ = webpack_bundle_tag('two_factor_auth')
.js-two-factor-auth{ 'data-two-factor-skippable' => "#{two_factor_skippable?}", 'data-two_factor_skip_url' => skip_profile_two_factor_auth_path }
.row.prepend-top-default
diff --git a/app/views/projects/_merge_request_fast_forward_settings.html.haml b/app/views/projects/_merge_request_fast_forward_settings.html.haml
index 8129c72feb2..f455522d17c 100644
--- a/app/views/projects/_merge_request_fast_forward_settings.html.haml
+++ b/app/views/projects/_merge_request_fast_forward_settings.html.haml
@@ -3,7 +3,7 @@
.radio
= label_tag :project_merge_method_ff do
- = form.radio_button :merge_method, :ff, class: "js-merge-method-radio"
+ = form.radio_button :merge_method, :ff, class: "js-merge-method-radio qa-radio-button-merge-ff"
%strong Fast-forward merge
%br
%span.descr
diff --git a/app/views/projects/_new_project_push_tip.html.haml b/app/views/projects/_new_project_push_tip.html.haml
new file mode 100644
index 00000000000..9bc69211d12
--- /dev/null
+++ b/app/views/projects/_new_project_push_tip.html.haml
@@ -0,0 +1,11 @@
+.push-to-create-popover
+ %p
+ = label_tag(:push_to_create_tip, _("Private projects can be created in your personal namespace with:"), class: "weight-normal")
+
+ %p.input-group.project-tip-command
+ %span.input-group-btn
+ = text_field_tag :push_to_create_tip, push_to_create_project_command, class: "js-select-on-focus form-control monospace", readonly: true, aria: { label: _("Push project from command line") }
+ %span.input-group-btn
+ = clipboard_button(text: push_to_create_project_command, title: _("Copy command to clipboard"), placement: "right")
+ %p
+ = link_to("What does this command do?", help_page_path("gitlab-basics/create-project", anchor: "push-to-create-a-new-project"), target: "_blank")
diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml
index aebdfbc8218..705338c083e 100644
--- a/app/views/projects/_readme.html.haml
+++ b/app/views/projects/_readme.html.haml
@@ -20,4 +20,4 @@
distributed with computer software, forming part of its documentation.
GitLab will render it here instead of this message.
%p
- = link_to "Add Readme", add_special_file_path(@project, file_name: 'README.md'), class: 'btn btn-new'
+ = link_to "Add Readme", @project.add_readme_path, class: 'btn btn-new'
diff --git a/app/views/projects/_stat_anchor_list.html.haml b/app/views/projects/_stat_anchor_list.html.haml
new file mode 100644
index 00000000000..a115b65938b
--- /dev/null
+++ b/app/views/projects/_stat_anchor_list.html.haml
@@ -0,0 +1,8 @@
+- anchors = local_assigns.fetch(:anchors, [])
+
+- return unless anchors.any?
+%ul.nav
+ - anchors.each do |anchor|
+ %li
+ = link_to_if anchor.link, anchor.label, anchor.link, class: anchor.enabled ? 'stat-link' : "btn btn-#{anchor.class_modifier || 'missing'}" do
+ %span.stat-text= anchor.label
diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml
index 2a77dedd9a2..1b150ec3e5c 100644
--- a/app/views/projects/blob/_header.html.haml
+++ b/app/views/projects/blob/_header.html.haml
@@ -11,8 +11,8 @@
= view_on_environment_button(@commit.sha, @path, @environment) if @environment
.btn-group{ role: "group" }<
- = edit_blob_link
- = ide_blob_link
+ = edit_blob_button
+ = ide_edit_button
- if current_user
= replace_blob_link
= delete_blob_link
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 21b6aa4bad9..f1324c61500 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -2,7 +2,7 @@
.modal-dialog.modal-lg
.modal-content
.modal-header
- %a.close{ href: "#", "data-dismiss" => "modal" } ×
+ %a.close{ href: "#", "data-dismiss" => "modal" } &times;
%h3.page-title= title
.modal-body
= form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal', data: { method: method } do
@@ -27,6 +27,3 @@
- unless can?(current_user, :push_code, @project)
.inline.prepend-left-10
= commit_in_fork_help
-
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('blob')
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 626cbc9e41d..9d90251ab66 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -3,7 +3,6 @@
- page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
- = page_specific_javascript_bundle_tag('blob')
%div{ class: container_class }
- if @conflict
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index a4263774dfd..fa091d8f6ef 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -2,7 +2,6 @@
- page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
- = page_specific_javascript_bundle_tag('blob')
.editor-title-row
%h3.page-title.blob-new-page-title
New file
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index 2ed454131af..efb8175398b 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -3,10 +3,6 @@
- page_title @blob.path, @ref
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'blob'
-
-
%div{ class: container_class }
= render 'projects/last_push'
diff --git a/app/views/projects/blob/viewers/_balsamiq.html.haml b/app/views/projects/blob/viewers/_balsamiq.html.haml
index 1e7c461f02e..15349387eb2 100644
--- a/app/views/projects/blob/viewers/_balsamiq.html.haml
+++ b/app/views/projects/blob/viewers/_balsamiq.html.haml
@@ -1,4 +1,4 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('balsamiq_viewer')
+ = webpack_bundle_tag('balsamiq_viewer')
.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: blob_raw_path } }
diff --git a/app/views/projects/blob/viewers/_notebook.html.haml b/app/views/projects/blob/viewers/_notebook.html.haml
index 8a41bc53004..d1ffaca35b9 100644
--- a/app/views/projects/blob/viewers/_notebook.html.haml
+++ b/app/views/projects/blob/viewers/_notebook.html.haml
@@ -1,5 +1,5 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('notebook_viewer')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('notebook_viewer')
.file-content#js-notebook-viewer{ data: { endpoint: blob_raw_path } }
diff --git a/app/views/projects/blob/viewers/_pdf.html.haml b/app/views/projects/blob/viewers/_pdf.html.haml
index ec2b18bd4ab..fc3f0d922b1 100644
--- a/app/views/projects/blob/viewers/_pdf.html.haml
+++ b/app/views/projects/blob/viewers/_pdf.html.haml
@@ -1,5 +1,5 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('pdf_viewer')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('pdf_viewer')
.file-content#js-pdf-viewer{ data: { endpoint: blob_raw_path } }
diff --git a/app/views/projects/blob/viewers/_sketch.html.haml b/app/views/projects/blob/viewers/_sketch.html.haml
index 775e4584f77..8fb67c819c1 100644
--- a/app/views/projects/blob/viewers/_sketch.html.haml
+++ b/app/views/projects/blob/viewers/_sketch.html.haml
@@ -1,6 +1,6 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('sketch_viewer')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('sketch_viewer')
.file-content#js-sketch-viewer{ data: { endpoint: blob_raw_path } }
.js-loading-icon.text-center.prepend-top-default.append-bottom-default.js-loading-icon{ 'aria-label' => 'Loading Sketch preview' }
diff --git a/app/views/projects/blob/viewers/_stl.html.haml b/app/views/projects/blob/viewers/_stl.html.haml
index 6578d826ace..e58809ec008 100644
--- a/app/views/projects/blob/viewers/_stl.html.haml
+++ b/app/views/projects/blob/viewers/_stl.html.haml
@@ -1,5 +1,5 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('stl_viewer')
+ = webpack_bundle_tag('stl_viewer')
.file-content.is-stl-loading
.text-center#js-stl-viewer{ data: { endpoint: blob_raw_path } }
diff --git a/app/views/projects/buttons/_koding.html.haml b/app/views/projects/buttons/_koding.html.haml
index de2d61d4aa3..e665ca61da8 100644
--- a/app/views/projects/buttons/_koding.html.haml
+++ b/app/views/projects/buttons/_koding.html.haml
@@ -1,3 +1,3 @@
-- if koding_enabled? && current_user && @repository.koding_yml && can_push_branch?(@project, @project.default_branch)
+- if koding_enabled? && current_user && @repository.koding_yml && @project.can_current_user_push_code?
= link_to koding_project_url(@project), class: 'btn project-action-button inline', target: '_blank', rel: 'noopener noreferrer' do
_('Run in IDE (Koding)')
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 0a54c736761..d8b4266143e 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
- if current_user
- = link_to toggle_star_project_path(@project), { class: 'btn star-btn toggle-star', method: :post, remote: true } do
+ %button.btn.btn-default.star-btn.toggle-star{ type: "button", data: { endpoint: toggle_star_project_path(@project, :json) } }
- if current_user.starred?(@project)
= sprite_icon('star')
%span.starred= _('Unstar')
diff --git a/app/views/projects/clusters/_empty_state.html.haml b/app/views/projects/clusters/_empty_state.html.haml
index 600d679b60c..112dde66ff7 100644
--- a/app/views/projects/clusters/_empty_state.html.haml
+++ b/app/views/projects/clusters/_empty_state.html.haml
@@ -4,7 +4,7 @@
.col-xs-12
.text-content
%h4.text-center= s_('ClusterIntegration|Integrate Kubernetes cluster automation')
- - link_to_help_page = link_to(s_('ClusterIntegration|Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
+ - link_to_help_page = link_to(_('Learn more about Kubernetes'), help_page_path('user/project/clusters/index'), target: '_blank', rel: 'noopener noreferrer')
%p= s_('ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}').html_safe % { link_to_help_page: link_to_help_page}
.text-center
diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml
index 49b0b314e1d..a3fed25af28 100644
--- a/app/views/projects/commit/_pipelines_list.haml
+++ b/app/views/projects/commit/_pipelines_list.haml
@@ -8,5 +8,4 @@
} }
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('commit_pipelines')
+ = webpack_bundle_tag('common_vue')
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index 2890e9d2b65..4058e61eb9a 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -7,8 +7,8 @@
- page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
- page_description @commit.description
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('diff_notes')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('diff_notes')
.container-fluid{ class: [limited_container_width, container_class] }
= render "commit_box"
diff --git a/app/views/projects/commits/_commit.atom.builder b/app/views/projects/commits/_commit.atom.builder
index 04914888763..50f7e7a3a33 100644
--- a/app/views/projects/commits/_commit.atom.builder
+++ b/app/views/projects/commits/_commit.atom.builder
@@ -3,7 +3,7 @@ xml.entry do
xml.link href: project_commit_url(@project, id: commit.id)
xml.title truncate(commit.title, length: 80, escape: false)
xml.updated commit.committed_date.xmlschema
- xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
+ xml.media :thumbnail, width: "40", height: "40", url: image_url(avatar_icon_for_email(commit.author_email))
xml.author do |author|
xml.name commit.author_name
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 64259669c19..078bd0eee63 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -20,7 +20,7 @@
.avatar-cell.hidden-xs
= author_avatar(commit, size: 36)
- .commit-detail
+ .commit-detail.flex-list
.commit-content
= link_to_markdown_field(commit, :title, link, class: "commit-row-message item-title")
%span.commit-row-message.visible-xs-inline
@@ -51,7 +51,7 @@
- if commit.status(ref)
= render_commit_status(commit, ref: ref)
- #commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } }
+ .js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } }
= link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link"
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit)
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index 71d30da14a9..d98e0564da4 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -1,8 +1,8 @@
- @no_container = true
- page_title "Cycle Analytics"
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('cycle_analytics')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('cycle_analytics')
#cycle-analytics{ class: container_class, "v-cloak" => "true", data: { request_path: project_cycle_analytics_path(@project) } }
- if @cycle_analytics_no_data
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 0b01e38d23d..47bfcb21cf4 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -17,7 +17,7 @@
\
- if editable_diff?(diff_file)
- link_opts = @merge_request.persisted? ? { from_merge_request_iid: @merge_request.iid } : {}
- = edit_blob_link(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
+ = edit_blob_button(@merge_request.source_project, @merge_request.source_branch, diff_file.new_path,
blob: blob, link_opts: link_opts)
- if image_diff && image_replaced
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index e16d132f869..b947b91322d 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -58,7 +58,7 @@
- if @project.avatar?
%hr
= link_to _('Remove avatar'), project_avatar_path(@project), data: { confirm: _("Avatar will be removed. Are you sure?") }, method: :delete, class: "btn btn-danger btn-inverted"
- = f.submit 'Save changes', class: "btn btn-success"
+ = f.submit 'Save changes', class: "btn btn-success js-btn-save-general-project-settings"
%section.settings.sharing-permissions.no-animate{ class: ('expanded' if expanded) }
.settings-header
@@ -85,7 +85,7 @@
.settings-content
= form_for [@project.namespace.becomes(Namespace), @project], remote: true, html: { multipart: true, class: "merge-request-settings-form" }, authenticity_token: true do |f|
= render 'merge_request_settings', form: f
- = f.submit 'Save changes', class: "btn btn-save"
+ = f.submit 'Save changes', class: "btn btn-save qa-save-merge-request-changes"
= render 'export', project: @project
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index ab225796b12..8a36fada389 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -5,38 +5,41 @@
= render "home_panel"
-.row-content-block.second-block.center
- %h4
- The repository for this project is empty
+.project-empty-note-panel
+ %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
+ .prepend-top-20
+ %h4
+ = _('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 }
- - if can?(current_user, :push_code, @project)
- %p
- If you already have files you can push them using command line instructions below.
- %p
- Otherwise you can start with adding a
- = succeed ',' do
- = link_to "README", add_special_file_path(@project, file_name: 'README.md')
- a
- = succeed ',' do
- = link_to "LICENSE", add_special_file_path(@project, file_name: 'LICENSE')
- or a
- = link_to '.gitignore', add_special_file_path(@project, file_name: '.gitignore')
- to this project.
- %p
- You will need to be owner or have the master permission level for the initial push, as the master branch is automatically protected.
+ %hr
+ %p
+ - link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-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 show_auto_devops_callout?(@project)
+ %hr
%p
- - link = link_to(s_('AutoDevOps|Auto DevOps (Beta)'), project_settings_ci_cd_path(@project, anchor: 'js-general-pipeline-settings'))
- = s_('AutoDevOps|You can activate %{link_to_settings} for this project.').html_safe % { link_to_settings: link }
- %p= s_('AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration.')
- %p= link_to _('New file'), project_new_blob_path(@project, @project.default_branch || 'master'), class: 'btn btn-new'
+ = _('Otherwise it is recommended you start with one of the options below.')
+ .prepend-top-20
+
+%nav.project-stats{ class: container_class }
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project)
- %div{ class: container_class }
+ %div{ class: [container_class, ("limit-container-width-sm" unless fluid_layout)] }
.prepend-top-20
.empty_wrapper
- %h3.page-title-empty
+ %h3#repo-command-line-instructions.page-title-empty
Command line instructions
.git-empty
%fieldset
diff --git a/app/views/projects/environments/folder.html.haml b/app/views/projects/environments/folder.html.haml
index d9c9f0ed546..d8054dbc372 100644
--- a/app/views/projects/environments/folder.html.haml
+++ b/app/views/projects/environments/folder.html.haml
@@ -2,8 +2,7 @@
- page_title "Environments"
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag("environments_folder")
+ = webpack_bundle_tag('common_vue')
#environments-folder-list-view{ data: { endpoint: folder_project_environments_path(@project, @folder, format: :json),
"folder-name" => @folder,
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index 88f1348da47..31cf173fa9c 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -3,8 +3,8 @@
- add_to_breadcrumbs("Pipelines", project_pipelines_path(@project))
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag("common_vue")
- = page_specific_javascript_bundle_tag("environments")
+ = webpack_bundle_tag("common_vue")
+ = webpack_bundle_tag("environments")
#environments-list-view{ data: { environments_data: environments_list_data,
"can-create-deployment" => can?(current_user, :create_deployment, @project).to_s,
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index 10812f67cbe..91b3743e9e7 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -2,7 +2,6 @@
- page_title "Metrics for environment", @environment.name
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'common_d3'
.prometheus-container{ class: container_class }
.top-area
diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml
index a073a164f11..7be4ef39117 100644
--- a/app/views/projects/environments/terminal.html.haml
+++ b/app/views/projects/environments/terminal.html.haml
@@ -3,7 +3,7 @@
- content_for :page_specific_javascripts do
= stylesheet_link_tag "xterm/xterm"
- = page_specific_javascript_bundle_tag("terminal")
+ = webpack_bundle_tag("terminal")
%div{ class: container_class }
.top-area
diff --git a/app/views/projects/graphs/charts.html.haml b/app/views/projects/graphs/charts.html.haml
index 300a39fe257..d4b4a6203f3 100644
--- a/app/views/projects/graphs/charts.html.haml
+++ b/app/views/projects/graphs/charts.html.haml
@@ -1,9 +1,5 @@
- @no_container = true
- page_title "Charts"
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('common_d3')
- = webpack_bundle_tag('graphs')
- = webpack_bundle_tag('graphs_charts')
.repo-charts{ class: container_class }
%h4.sub-header
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index cce16bc58b3..c81ee6874e3 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,9 +1,5 @@
- @no_container = true
- page_title _('Contributors')
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('common_d3')
- = webpack_bundle_tag('graphs')
- = webpack_bundle_tag('graphs_show')
.js-graphs-show{ class: container_class, 'data-project-graph-path': project_graph_path(@project, current_ref, format: :json) }
.sub-header-block
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 9779c1985d5..11b5e02f1e0 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -12,6 +12,8 @@
markdown_docs_path: help_page_path('user/markdown'),
quick_actions_docs_path: help_page_path('user/project/quick_actions'),
notes_path: notes_url,
+ close_issue_path: issue_path(@issue, issue: { state_event: :close }, format: 'json'),
+ reopen_issue_path: issue_path(@issue, issue: { state_event: :reopen }, format: 'json'),
last_fetched_at: Time.now.to_i,
noteable_data: serialize_issuable(@issue),
current_user_data: UserSerializer.new.represent(current_user, only_path: true).to_json } }
diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml
index 37b00a14fc6..36e24037214 100644
--- a/app/views/projects/issues/_new_branch.html.haml
+++ b/app/views/projects/issues/_new_branch.html.haml
@@ -21,30 +21,33 @@
%button.btn.create-merge-request-dropdown-toggle.dropdown-toggle.btn-success.btn-inverted.js-dropdown-toggle{ type: 'button', data: { dropdown: { trigger: '#create-merge-request-dropdown' } } }
= icon('caret-down')
- %ul#create-merge-request-dropdown.create-merge-request-dropdown-menu.dropdown-menu.dropdown-menu-align-right.gl-show-field-errors{ data: { dropdown: true } }
- - if can_create_merge_request
- %li.create-item.droplab-item-selected.droplab-item-ignore-hiding{ role: 'button', data: { value: 'create-mr', text: 'Create merge request' } }
- .menu-item.droplab-item-ignore-hiding
- .icon-container.droplab-item-ignore-hiding= icon('check')
- .description.droplab-item-ignore-hiding Create merge request and branch
-
- %li.create-item.droplab-item-ignore-hiding{ class: [!can_create_merge_request && 'droplab-item-selected'], role: 'button', data: { value: 'create-branch', text: 'Create branch' } }
- .menu-item.droplab-item-ignore-hiding
- .icon-container.droplab-item-ignore-hiding= icon('check')
- .description.droplab-item-ignore-hiding Create branch
- %li.divider
-
- %li.droplab-item-ignore
- Branch name
- %input.js-branch-name.form-control.droplab-item-ignore{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" }
- %span.js-branch-message.branch-message.droplab-item-ignore
-
- %li.droplab-item-ignore
- Source (branch or tag)
- %input.js-ref.ref.form-control.droplab-item-ignore{ type: 'text', placeholder: "#{@project.default_branch}", value: "#{@project.default_branch}", data: { value: "#{@project.default_branch}" } }
- %span.js-ref-message.ref-message.droplab-item-ignore
-
- %li.droplab-item-ignore
- %button.btn.btn-success.js-create-target.droplab-item-ignore{ type: 'button', data: { action: 'create-mr' } }
- Create merge request
-
+ .droplab-dropdown
+ %ul#create-merge-request-dropdown.create-merge-request-dropdown-menu.dropdown-menu.dropdown-menu-align-right.gl-show-field-errors{ data: { dropdown: true } }
+ - if can_create_merge_request
+ %li.droplab-item-selected{ role: 'button', data: { value: 'create-mr', text: _('Create merge request') } }
+ .menu-item
+ = icon('check', class: 'icon')
+ = _('Create merge request and branch')
+
+ %li{ class: [!can_create_merge_request && 'droplab-item-selected'], role: 'button', data: { value: 'create-branch', text: _('Create branch') } }
+ .menu-item
+ = icon('check', class: 'icon')
+ = _('Create branch')
+ %li.divider.droplab-item-ignore
+
+ %li.droplab-item-ignore.prepend-left-8.append-right-8.prepend-top-16
+ .form-group
+ %label{ for: 'new-branch-name' }
+ = _('Branch name')
+ %input#new-branch-name.js-branch-name.form-control{ type: 'text', placeholder: "#{@issue.to_branch_name}", value: "#{@issue.to_branch_name}" }
+ %span.js-branch-message.help-block
+
+ .form-group
+ %label{ for: 'source-name' }
+ = _('Source (branch or tag)')
+ %input#source-name.js-ref.ref.form-control{ type: 'text', placeholder: "#{@project.default_branch}", value: "#{@project.default_branch}", data: { value: "#{@project.default_branch}" } }
+ %span.js-ref-message.help-block
+
+ .form-group
+ %button.btn.btn-success.js-create-target{ type: 'button', data: { action: 'create-mr' } }
+ = _('Create merge request')
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 193111b4cee..fb06ba58c27 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -6,7 +6,6 @@
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'filtered_search'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, params.merge(rss_url_options), title: "#{@project.name} issues")
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 1f28d8acff6..d63443c9da5 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -5,10 +5,6 @@
- page_description @issue.description
- page_card_attributes @issue.card_attributes
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'notes'
-
- can_update_issue = can?(current_user, :update_issue, @issue)
- can_report_spam = @issue.submittable_as_spam_by?(current_user)
@@ -59,7 +55,7 @@
.issue-details.issuable-details
.detail-page-description.content-block
- %script#js-issuable-app-initial-data{ type: "application/json" }= issuable_initial_data(@issue)
+ %script#js-issuable-app-initial-data{ type: "application/json" }= issuable_initial_data(@issue).to_json
#js-issuable-app
%h2.title= markdown_field(@issue, :title)
- if @issue.description.present?
@@ -86,6 +82,3 @@
= render 'projects/issues/discussion'
= render 'shared/issuable/sidebar', issuable: @issue
-
-= page_specific_javascript_bundle_tag('common_vue')
-= page_specific_javascript_bundle_tag('issue_show')
diff --git a/app/views/projects/jobs/_user.html.haml b/app/views/projects/jobs/_user.html.haml
index 83f299da651..461d503f95d 100644
--- a/app/views/projects/jobs/_user.html.haml
+++ b/app/views/projects/jobs/_user.html.haml
@@ -1,7 +1,7 @@
by
%a{ href: user_path(@build.user) }
%span.hidden-xs
- = image_tag avatar_icon(@build.user, 24), class: "avatar s24"
+ = image_tag avatar_icon_for_user(@build.user, 24), class: "avatar s24"
%strong{ data: { toggle: 'tooltip', placement: 'top', title: @build.user.to_reference } }
= @build.user.name
%strong.visible-xs-inline= @build.user.to_reference
diff --git a/app/views/projects/jobs/show.html.haml b/app/views/projects/jobs/show.html.haml
index 93efa7e8e86..849c273db8c 100644
--- a/app/views/projects/jobs/show.html.haml
+++ b/app/views/projects/jobs/show.html.haml
@@ -112,7 +112,3 @@
.js-build-options{ data: javascript_build_options }
#js-job-details-vue{ data: { endpoint: project_job_path(@project, @build, format: :json) } }
-
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('common_vue')
- = webpack_bundle_tag('job_details')
diff --git a/app/views/projects/merge_requests/_how_to_merge.html.haml b/app/views/projects/merge_requests/_how_to_merge.html.haml
index 917ec7fdbda..54a661040ea 100644
--- a/app/views/projects/merge_requests/_how_to_merge.html.haml
+++ b/app/views/projects/merge_requests/_how_to_merge.html.haml
@@ -1,6 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('how_to_merge')
-
#modal_merge_info.modal
.modal-dialog
.modal-content
diff --git a/app/views/projects/merge_requests/conflicts.html.haml b/app/views/projects/merge_requests/conflicts.html.haml
index 454bc359b6b..2a2e57027be 100644
--- a/app/views/projects/merge_requests/conflicts.html.haml
+++ b/app/views/projects/merge_requests/conflicts.html.haml
@@ -1,7 +1,7 @@
- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('merge_conflicts')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('merge_conflicts')
= page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title"
diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml
index 454bc359b6b..2a2e57027be 100644
--- a/app/views/projects/merge_requests/conflicts/show.html.haml
+++ b/app/views/projects/merge_requests/conflicts/show.html.haml
@@ -1,7 +1,7 @@
- page_title "Merge Conflicts", "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('merge_conflicts')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('merge_conflicts')
= page_specific_javascript_tag('lib/ace.js')
= render "projects/merge_requests/mr_title"
diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml
index 640d2791dc1..720ba236434 100644
--- a/app/views/projects/merge_requests/index.html.haml
+++ b/app/views/projects/merge_requests/index.html.haml
@@ -8,7 +8,6 @@
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'filtered_search'
%div{ class: container_class }
= render 'projects/last_push'
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 8740c6895df..e29f21b3bec 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -6,7 +6,6 @@
- page_card_attributes @merge_request.card_attributes
- content_for :page_specific_javascripts do
= webpack_bundle_tag('common_vue')
- = webpack_bundle_tag('diff_notes')
.merge-request{ data: { mr_action: j(params[:tab].presence || 'show'), url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project) } }
= render "projects/merge_requests/mr_title"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 623c42ba88e..de381d489c6 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -27,7 +27,7 @@
Edit
- if @project.group
- = link_to promote_project_milestone_path(@milestone.project, @milestone), title: "Promote to Group Milestone", class: 'btn btn-grouped', data: { confirm: "You are about to promote #{@milestone.title} to a group level. This will make this milestone available to all projects inside #{@project.group.name}. The existing project milestone will be merged into the group level. This action cannot be reversed.", toggle: "tooltip" }, method: :post do
+ = link_to promote_project_milestone_path(@milestone.project, @milestone), title: "Promote to Group Milestone", class: 'btn btn-grouped', data: { confirm: "Promoting #{@milestone.title} will make it available for all projects inside #{@project.group.name}. Existing project milestones with the same name will be merged. This action cannot be reversed.", toggle: "tooltip" }, method: :post do
Promote
- if @milestone.active?
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index 2efb7fc719f..97be8950db0 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,7 +1,7 @@
- breadcrumb_title "Graph"
- page_title "Graph", @ref
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('network')
+ = webpack_bundle_tag('network')
= render "head"
%div{ class: container_class }
.project-network
diff --git a/app/views/projects/network/show.json.erb b/app/views/projects/network/show.json.erb
index 7491b37310d..a0e82e891ff 100644
--- a/app/views/projects/network/show.json.erb
+++ b/app/views/projects/network/show.json.erb
@@ -9,7 +9,7 @@
author: {
name: c.author_name,
email: c.author_email,
- icon: image_path(avatar_icon(c.author_email, 20))
+ icon: image_path(avatar_icon_for_email(c.author_email, 20))
},
time: c.time,
space: c.spaces.first,
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 61ae0ebbce6..679ba23a4db 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -4,6 +4,7 @@
- page_title 'New Project'
- header_title "Projects", dashboard_projects_path
- visibility_level = params.dig(:project, :visibility_level) || default_project_visibility
+- active_tab = local_assigns.fetch(:active_tab, 'blank')
.project-edit-container
.project-edit-errors
@@ -18,34 +19,41 @@
All features are enabled when you create a project, but you can disable the ones you don’t need in the project settings.
.md
= brand_new_project_guidelines
+ %p
+ %strong= _("Tip:")
+ = _("You can also create a project from the command line.")
+ %a.push-new-project-tip{ data: { title: _("Push to create a project") }, href: help_page_path('gitlab-basics/create-project', anchor: 'push-to-create-a-new-project'), target: "_blank", rel: "noopener noreferrer" }
+ = _("Show command")
+ %template.push-new-project-tip-template= render partial: "new_project_push_tip"
+
.col-lg-9.js-toggle-container
%ul.nav-links.gitlab-tabs{ role: 'tablist' }
- %li.active{ role: 'presentation' }
+ %li{ class: ('active' if active_tab == 'blank'), role: 'presentation' }
%a{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Blank project
%span.visible-xs Blank
- %li{ role: 'presentation' }
+ %li{ class: ('active' if active_tab == 'template'), role: 'presentation' }
%a{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Create from template
%span.visible-xs Template
- %li{ role: 'presentation' }
+ %li{ class: ('active' if active_tab == 'import'), role: 'presentation' }
%a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' }
%span.hidden-xs Import project
%span.visible-xs Import
.tab-content.gitlab-tab-content
- .tab-pane.active{ id: 'blank-project-pane', role: 'tabpanel' }
+ .tab-pane{ id: 'blank-project-pane', class: ('active' if active_tab == 'blank'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f|
= render 'new_project_fields', f: f, project_name_id: "blank-project-name"
- .tab-pane.no-padding{ id: 'create-from-template-pane', role: 'tabpanel' }
+ .tab-pane.no-padding{ id: 'create-from-template-pane', class: ('active' if active_tab == 'template'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f|
.project-template
.form-group
%div
= render 'project_templates', f: f
- .tab-pane.import-project-pane{ id: 'import-project-pane', role: 'tabpanel' }
+ .tab-pane.import-project-pane{ id: 'import-project-pane', class: ('active' if active_tab == 'import'), role: 'tabpanel' }
= form_for @project, html: { class: 'new_project' } do |f|
- if import_sources_enabled?
.project-import.row
@@ -92,7 +100,7 @@
%button.btn.js-toggle-button.import_git{ type: "button" }
= icon('git', text: 'Repo by URL')
.col-lg-12
- .js-toggle-content.hide.toggle-import-form
+ .js-toggle-content.toggle-import-form{ class: ('hide' if active_tab != 'import') }
%hr
= render "shared/import_form", f: f
= render 'new_project_fields', f: f, project_name_id: "import-url-name"
diff --git a/app/views/projects/pages/_list.html.haml b/app/views/projects/pages/_list.html.haml
index a85cda407af..75df92b05a7 100644
--- a/app/views/projects/pages/_list.html.haml
+++ b/app/views/projects/pages/_list.html.haml
@@ -3,15 +3,26 @@
.panel-heading
Domains (#{@domains.count})
%ul.well-list
+ - verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
- @domains.each do |domain|
%li
.pull-right
= link_to 'Details', project_pages_domain_path(@project, domain), class: "btn btn-sm btn-grouped"
= link_to 'Remove', project_pages_domain_path(@project, domain), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped"
.clearfix
- %span= link_to domain.domain, domain.url
+ - if verification_enabled
+ - tooltip, status = domain.unverified? ? ['Unverified', 'failed'] : ['Verified', 'success']
+ = link_to domain.url, title: tooltip, class: 'has-tooltip' do
+ = sprite_icon("status_#{status}", size: 16, css_class: "has-tooltip ci-status-icon ci-status-icon-#{status}")
+ = domain.domain
+ - else
+ = link_to domain.domain, domain.url
%p
- if domain.subject
%span.label.label-gray Certificate: #{domain.subject}
- if domain.expired?
%span.label.label-danger Expired
+ - if verification_enabled && domain.unverified?
+ %li.warning-row
+ #{domain.domain} is not verified. To learn how to verify ownership, visit your
+ = link_to 'domain details', project_pages_domain_path(@project, domain)
diff --git a/app/views/projects/pages_domains/show.html.haml b/app/views/projects/pages_domains/show.html.haml
index 876cac0dacb..72e9203bdb0 100644
--- a/app/views/projects/pages_domains/show.html.haml
+++ b/app/views/projects/pages_domains/show.html.haml
@@ -1,4 +1,10 @@
- page_title "#{@domain.domain}", 'Pages Domains'
+- verification_enabled = Gitlab::CurrentSettings.pages_domain_verification_enabled?
+- if verification_enabled && @domain.unverified?
+ %p.alert.alert-warning
+ %strong
+ This domain is not verified. You will need to verify ownership before
+ access is enabled.
%h3.page-title
Pages Domain
@@ -15,9 +21,26 @@
DNS
%td
%p
- To access the domain create a new DNS record:
+ To access this domain create a new DNS record:
%pre
#{@domain.domain} CNAME #{@domain.project.pages_subdomain}.#{Settings.pages.host}.
+ - if verification_enabled
+ %tr
+ %td
+ Verification status
+ %td
+ %p
+ - help_link = help_page_path('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ To #{link_to 'verify ownership', help_link} of your domain, create
+ this DNS record:
+ %pre
+ #{@domain.verification_domain} TXT #{@domain.keyed_verification_code}
+ %p
+ - if @domain.verified?
+ #{@domain.domain} has been successfully verified.
+ - else
+ = button_to 'Verify ownership', verify_project_pages_domain_path(@project, @domain), class: 'btn btn-save btn-sm'
+
%tr
%td
Certificate
diff --git a/app/views/projects/pipeline_schedules/_form.html.haml b/app/views/projects/pipeline_schedules/_form.html.haml
index ff440e99042..160e325996a 100644
--- a/app/views/projects/pipeline_schedules/_form.html.haml
+++ b/app/views/projects/pipeline_schedules/_form.html.haml
@@ -1,7 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'schedule_form'
-
= form_for [@project.namespace.becomes(Namespace), @project, @schedule], as: :schedule, html: { id: "new-pipeline-schedule-form", class: "form-horizontal js-pipeline-schedule-form" } do |f|
= form_errors(@schedule)
.form-group
diff --git a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
index a8692b83b07..55d0e8bb7f9 100644
--- a/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
+++ b/app/views/projects/pipeline_schedules/_pipeline_schedule.html.haml
@@ -21,7 +21,7 @@
= s_("PipelineSchedules|Inactive")
%td
- if pipeline_schedule.owner
- = image_tag avatar_icon(pipeline_schedule.owner, 20), class: "avatar s20"
+ = image_tag avatar_icon_for_user(pipeline_schedule.owner, 20), class: "avatar s20"
= link_to user_path(pipeline_schedule.owner) do
= pipeline_schedule.owner&.name
%td
diff --git a/app/views/projects/pipeline_schedules/index.html.haml b/app/views/projects/pipeline_schedules/index.html.haml
index 4fbdd1dd5e4..bcb6dddba1a 100644
--- a/app/views/projects/pipeline_schedules/index.html.haml
+++ b/app/views/projects/pipeline_schedules/index.html.haml
@@ -1,9 +1,5 @@
- breadcrumb_title _("Schedules")
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'schedules_index'
-
- @no_container = true
- page_title _("Pipeline Schedules")
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 5de17977d5a..852143ecb2a 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -3,16 +3,16 @@
.tabs-holder
%ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator
%li.js-pipeline-tab-link
- = link_to project_pipeline_path(@project, @pipeline), data: { target: 'div#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
- Pipeline
+ = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
+ = _("Pipeline")
%li.js-builds-tab-link
- = link_to builds_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
- Jobs
+ = link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
+ = _("Jobs")
%span.badge.js-builds-counter= pipeline.total_size
- if failed_builds.present?
%li.js-failures-tab-link
- = link_to failures_project_pipeline_path(@project, @pipeline), data: {target: 'div#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
- Failed Jobs
+ = link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
+ = _("Failed Jobs")
%span.badge.js-failures-counter= failed_builds.count
.tab-content
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index ba55bc23add..a86cb14960a 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -1,9 +1,6 @@
- @no_container = true
- breadcrumb_title "CI / CD Charts"
- page_title _("Charts"), _("Pipelines")
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_d3')
- = page_specific_javascript_bundle_tag('graphs')
%div{ class: container_class }
.sub-header-block
diff --git a/app/views/projects/pipelines/charts/_pipeline_times.haml b/app/views/projects/pipelines/charts/_pipeline_times.haml
index a5dbd1b1532..510697c2ae9 100644
--- a/app/views/projects/pipelines/charts/_pipeline_times.haml
+++ b/app/views/projects/pipelines/charts/_pipeline_times.haml
@@ -1,6 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('pipelines_times')
-
%div
%p.light
= _("Commit duration in minutes for last 30 commits")
diff --git a/app/views/projects/pipelines/charts/_pipelines.haml b/app/views/projects/pipelines/charts/_pipelines.haml
index 41dc2f6cf9d..2f4b6def155 100644
--- a/app/views/projects/pipelines/charts/_pipelines.haml
+++ b/app/views/projects/pipelines/charts/_pipelines.haml
@@ -1,6 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('pipelines_charts')
-
%h4= _("Pipelines charts")
%p
&nbsp;
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index f8555f11aab..fdcc60f48a5 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -13,5 +13,5 @@
"ci-lint-path" => ci_lint_path,
"reset-cache-path" => reset_cache_project_settings_ci_cd_path(@project) } }
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('pipelines')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('pipelines')
diff --git a/app/views/projects/protected_branches/_index.html.haml b/app/views/projects/protected_branches/_index.html.haml
index 2f30fe33a97..127a338e413 100644
--- a/app/views/projects/protected_branches/_index.html.haml
+++ b/app/views/projects/protected_branches/_index.html.haml
@@ -1,5 +1,5 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('protected_branches')
+ = webpack_bundle_tag('protected_branches')
- content_for :create_protected_branch do
= render 'projects/protected_branches/create_protected_branch'
diff --git a/app/views/projects/protected_tags/_index.html.haml b/app/views/projects/protected_tags/_index.html.haml
index 955220562a6..74f7f63c941 100644
--- a/app/views/projects/protected_tags/_index.html.haml
+++ b/app/views/projects/protected_tags/_index.html.haml
@@ -1,5 +1,5 @@
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('protected_tags')
+ = webpack_bundle_tag('protected_tags')
- content_for :create_protected_tag do
= render 'projects/protected_tags/create_protected_tag'
diff --git a/app/views/projects/registry/repositories/index.html.haml b/app/views/projects/registry/repositories/index.html.haml
index 36ea5e013e4..744b88760bc 100644
--- a/app/views/projects/registry/repositories/index.html.haml
+++ b/app/views/projects/registry/repositories/index.html.haml
@@ -14,8 +14,8 @@
.col-lg-12
#js-vue-registry-images{ data: { endpoint: project_container_registry_index_path(@project, format: :json) } }
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('registry_list')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('registry_list')
.row.prepend-top-10
.col-lg-12
diff --git a/app/views/projects/repositories/_feed.html.haml b/app/views/projects/repositories/_feed.html.haml
index 170f9e259df..87895a15239 100644
--- a/app/views/projects/repositories/_feed.html.haml
+++ b/app/views/projects/repositories/_feed.html.haml
@@ -11,7 +11,7 @@
%div
= link_to project_commits_path(@project, commit.id) do
%code= commit.short_id
- = image_tag avatar_icon(commit.author_email), class: "", width: 16, alt: ''
+ = image_tag avatar_icon_for_email(commit.author_email), class: "", width: 16, alt: ''
= markdown(truncate(commit.title, length: 40), pipeline: :single_line, author: commit.author)
%td
%span.pull-right.cgray
diff --git a/app/views/projects/runners/_shared_runners.html.haml b/app/views/projects/runners/_shared_runners.html.haml
index b037b57e78a..4fd4ca355a8 100644
--- a/app/views/projects/runners/_shared_runners.html.haml
+++ b/app/views/projects/runners/_shared_runners.html.haml
@@ -1,6 +1,6 @@
%h3 Shared Runners
-.bs-callout.bs-callout-warning.shared-runners-description
+.bs-callout.shared-runners-description
- if Gitlab::CurrentSettings.shared_runners_text.present?
= markdown_field(Gitlab::CurrentSettings.current_application_settings, :shared_runners_text)
- else
@@ -9,7 +9,7 @@
on GitLab.com).
%hr
- if @project.shared_runners_enabled?
- = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-warning', method: :post do
+ = link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-close', method: :post do
Disable shared Runners
- else
= link_to toggle_shared_runners_project_runners_path(@project), class: 'btn btn-success', method: :post do
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 28ccbf7eb15..f0813e56b71 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -1,8 +1,7 @@
%h3 Specific Runners
-= render partial: 'ci/runner/how_to_setup_runner',
- locals: { registration_token: @project.runners_token,
- type: 'specific' }
+= render partial: 'ci/runner/how_to_setup_specific_runner',
+ locals: { registration_token: @project.runners_token }
- if @project_runners.any?
%h4.underlined-title Runners activated for this project
diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml
index 0808b28a9df..17e804d682b 100644
--- a/app/views/projects/services/_form.html.haml
+++ b/app/views/projects/services/_form.html.haml
@@ -1,6 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('integrations')
-
.row.prepend-top-default.append-bottom-default
.col-lg-3
%h4.prepend-top-0
diff --git a/app/views/projects/services/prometheus/_configuration_banner.html.haml b/app/views/projects/services/prometheus/_configuration_banner.html.haml
new file mode 100644
index 00000000000..2cc2a6b2b5b
--- /dev/null
+++ b/app/views/projects/services/prometheus/_configuration_banner.html.haml
@@ -0,0 +1,26 @@
+%h4
+ = s_('PrometheusService|Auto configuration')
+
+- if service.manual_configuration?
+ .well
+ = s_('PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below')
+- else
+ .container-fluid
+ .row
+ - if service.prometheus_installed?
+ .col-sm-2
+ .svg-container
+ = image_tag 'illustrations/monitoring/getting_started.svg'
+ .col-sm-10
+ %p.text-success.prepend-top-default
+ = s_('PrometheusService|Prometheus is being automatically managed on your clusters')
+ = link_to s_('PrometheusService|Manage clusters'), project_clusters_path(project), class: 'btn'
+ - else
+ .col-sm-2
+ = image_tag 'illustrations/monitoring/loading.svg'
+ .col-sm-10
+ %p.prepend-top-default
+ = s_('PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments')
+ = link_to s_('PrometheusService|Install Prometheus on clusters'), project_clusters_path(project), class: 'btn btn-success'
+
+%hr
diff --git a/app/views/projects/services/prometheus/_help.html.haml b/app/views/projects/services/prometheus/_help.html.haml
index 5e320a252d8..88acb824ba7 100644
--- a/app/views/projects/services/prometheus/_help.html.haml
+++ b/app/views/projects/services/prometheus/_help.html.haml
@@ -1,29 +1,5 @@
-%h4
- = s_('PrometheusService|Auto configuration')
-
-- if @service.manual_configuration?
- .well
- = s_('PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below')
-- else
- .container-fluid
- .row
- - if @service.prometheus_installed?
- .col-sm-2
- .svg-container
- = image_tag 'illustrations/monitoring/getting_started.svg'
- .col-sm-10
- %p.text-success.prepend-top-default
- = s_('PrometheusService|Prometheus is being automatically managed on your clusters')
- = link_to s_('PrometheusService|Manage clusters'), project_clusters_path(@project), class: 'btn'
- - else
- .col-sm-2
- = image_tag 'illustrations/monitoring/loading.svg'
- .col-sm-10
- %p.prepend-top-default
- = s_('PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments')
- = link_to s_('PrometheusService|Install Prometheus on clusters'), project_clusters_path(@project), class: 'btn btn-success'
-
-%hr
+- if @project
+ = render 'projects/services/prometheus/configuration_banner', project: @project, service: @service
%h4.append-bottom-default
= s_('PrometheusService|Manual configuration')
diff --git a/app/views/projects/services/prometheus/_show.html.haml b/app/views/projects/services/prometheus/_show.html.haml
index b0cb5ce5e8f..6dc2b85fd32 100644
--- a/app/views/projects/services/prometheus/_show.html.haml
+++ b/app/views/projects/services/prometheus/_show.html.haml
@@ -1,6 +1,3 @@
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag('prometheus_metrics')
-
.row.prepend-top-default.append-bottom-default.prometheus-metrics-monitoring.js-prometheus-metrics-monitoring
.col-lg-3
%h4.prepend-top-0
@@ -10,7 +7,7 @@
= link_to s_('PrometheusService|More information'), help_page_path('user/project/integrations/prometheus')
.col-lg-9
- .panel.panel-default.js-panel-monitored-metrics{ data: { "active-metrics" => "#{project_prometheus_active_metrics_path(@project, :json)}" } }
+ .panel.panel-default.js-panel-monitored-metrics{ data: { active_metrics: active_common_project_prometheus_metrics_path(@project, :json) } }
.panel-heading
%h3.panel-title
= s_('PrometheusService|Monitored')
diff --git a/app/views/projects/settings/repository/show.html.haml b/app/views/projects/settings/repository/show.html.haml
index 517d51993d2..235d532bf98 100644
--- a/app/views/projects/settings/repository/show.html.haml
+++ b/app/views/projects/settings/repository/show.html.haml
@@ -3,8 +3,7 @@
- @content_class = "limit-container-width" unless fluid_layout
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('deploy_keys')
+ = webpack_bundle_tag('common_vue')
-# Protected branches & tags use a lot of nested partials.
-# The shared parts of the views can be found in the `shared` directory.
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 888d820b04e..fa281327eb7 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,6 +1,7 @@
- @no_container = true
- breadcrumb_title "Details"
- @content_class = "limit-container-width" unless fluid_layout
+- show_auto_devops_callout = show_auto_devops_callout?(@project)
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, project_path(@project, rss_url_options), title: "#{@project.name} activity")
@@ -14,65 +15,9 @@
- if can?(current_user, :download_code, @project)
%nav.project-stats{ class: container_class }
- %ul.nav
- %li
- = link_to project_tree_path(@project) do
- #{_('Files')} (#{storage_counter(@project.statistics.total_repository_size)})
- %li
- = link_to project_commits_path(@project, current_ref) do
- #{n_('Commit', 'Commits', @project.statistics.commit_count)} (#{number_with_delimiter(@project.statistics.commit_count)})
- %li
- = link_to project_branches_path(@project) do
- #{n_('Branch', 'Branches', @repository.branch_count)} (#{number_with_delimiter(@repository.branch_count)})
- %li
- = link_to project_tags_path(@project) do
- #{n_('Tag', 'Tags', @repository.tag_count)} (#{number_with_delimiter(@repository.tag_count)})
+ = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
+ = render 'stat_anchor_list', anchors: @project.statistics_buttons(show_auto_devops_callout: show_auto_devops_callout)
- - if @repository.readme
- %li
- = link_to _('Readme'),
- default_project_view != 'readme' ? readme_path(@project) : '#readme'
-
- - if @repository.changelog
- %li
- = link_to _('Changelog'), changelog_path(@project)
-
- - if @repository.license_blob
- %li
- = link_to license_short_name(@project), license_path(@project)
-
- - if @repository.contribution_guide
- %li
- = link_to _('Contribution guide'), contribution_guide_path(@project)
-
- - if @repository.gitlab_ci_yml
- %li
- = link_to _('CI/CD configuration'), ci_configuration_path(@project)
-
- - if current_user && can_push_branch?(@project, @project.default_branch)
- - unless @repository.changelog
- %li.missing
- = link_to add_special_file_path(@project, file_name: 'CHANGELOG') do
- #{ _('Add Changelog') }
- - unless @repository.license_blob
- %li.missing
- = link_to add_special_file_path(@project, file_name: 'LICENSE') do
- #{ _('Add License') }
- - unless @repository.contribution_guide
- %li.missing
- = link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
- #{ _('Add Contribution guide') }
- - unless @repository.gitlab_ci_yml
- %li.missing
- = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
- #{ _('Set up CI/CD') }
- - if koding_enabled? && @repository.koding_yml.blank?
- %li.missing
- = link_to _('Set up Koding'), add_koding_stack_path(@project)
- - if @repository.gitlab_ci_yml.blank? && @project.deployment_platform.present?
- %li.missing
- = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml', commit_message: 'Set up auto deploy', branch_name: 'auto-deploy', context: 'autodeploy') do
- #{ _('Set up auto deploy') }
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
- if @project.archived?
@@ -81,7 +26,7 @@
= icon("exclamation-triangle fw")
#{ _('Archived project! Repository is read-only') }
- - view_path = default_project_view
+ - view_path = @project.default_view
- if show_auto_devops_callout?(@project)
= render 'shared/auto_devops_callout'
diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml
index 05539dfed7c..39511435508 100644
--- a/app/views/projects/tree/_tree_header.html.haml
+++ b/app/views/projects/tree/_tree_header.html.haml
@@ -75,7 +75,7 @@
- if show_new_ide?
= succeed " " do
= link_to ide_edit_path(@project, @id), class: 'btn btn-default' do
- = ide_edit_text
+ = _('Web IDE')
= link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn'
diff --git a/app/views/projects/update.js.haml b/app/views/projects/update.js.haml
index 2c05ebe52ae..1a353953838 100644
--- a/app/views/projects/update.js.haml
+++ b/app/views/projects/update.js.haml
@@ -6,4 +6,4 @@
$(".project-edit-errors").html("#{escape_javascript(render('errors'))}");
$('.save-project-loader').hide();
$('.project-edit-container').show();
- $('.edit-project .btn-save').enable();
+ $('.edit-project .js-btn-save-general-project-settings').enable();
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index c4a5131c1a7..57a0b64bfd5 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -7,7 +7,7 @@
= snippet.title
by
= link_to user_snippets_path(snippet.author) do
- = image_tag avatar_icon(snippet.author), class: "avatar avatar-inline s16", alt: ''
+ = image_tag avatar_icon_for_user(snippet.author), class: "avatar avatar-inline s16", alt: ''
= snippet.author_name
%span.light= time_ago_with_tooltip(snippet.created_at)
%h4.snippet-title
diff --git a/app/views/search/results/_snippet_title.html.haml b/app/views/search/results/_snippet_title.html.haml
index aef825691e0..65710c09a89 100644
--- a/app/views/search/results/_snippet_title.html.haml
+++ b/app/views/search/results/_snippet_title.html.haml
@@ -18,6 +18,6 @@
%span
by
= link_to user_snippets_path(snippet_title.author) do
- = image_tag avatar_icon(snippet_title.author), class: "avatar avatar-inline s16", alt: ''
+ = image_tag avatar_icon_for_user(snippet_title.author), class: "avatar avatar-inline s16", alt: ''
= snippet_title.author_name
%span.light= time_ago_with_tooltip(snippet_title.created_at)
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index c0eebdfaddd..8847d11f623 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -48,7 +48,7 @@
.pull-right.hidden-xs.hidden-sm
- if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
- = link_to promote_project_label_path(label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "You are about to promote #{label.title} to a group level. This will make this milestone available to all projects inside #{label.project.group.name}. The existing project label will be merged into the group level. This action cannot be reversed.", toggle: "tooltip"}, method: :post do
+ = link_to promote_project_label_path(label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting #{label.title} will make it available for all projects inside #{label.project.group.name}. Existing project labels with the same name will be merged. This action cannot be reversed.", toggle: "tooltip"}, method: :post do
%span.sr-only Promote to Group
= sprite_icon('level-up')
- if can?(current_user, :admin_label, label)
diff --git a/app/views/shared/boards/_show.html.haml b/app/views/shared/boards/_show.html.haml
index ee8ad8e3999..3312254f5fb 100644
--- a/app/views/shared/boards/_show.html.haml
+++ b/app/views/shared/boards/_show.html.haml
@@ -6,8 +6,6 @@
- content_for :page_specific_javascripts do
= webpack_bundle_tag 'common_vue'
- = webpack_bundle_tag 'filtered_search'
- = webpack_bundle_tag 'boards'
%script#js-board-template{ type: "text/x-template" }= render "shared/boards/components/board"
%script#js-board-modal-filter{ type: "text/x-template" }= render "shared/issuable/search_bar", type: :boards_modal
diff --git a/app/views/shared/groups/_dropdown.html.haml b/app/views/shared/groups/_dropdown.html.haml
index 1a259b679c7..8607be9cd06 100644
--- a/app/views/shared/groups/_dropdown.html.haml
+++ b/app/views/shared/groups/_dropdown.html.haml
@@ -23,11 +23,11 @@
- if show_archive_options
%li.divider
%li.js-filter-archived-projects
- = link_to group_children_path(@group, archived: nil), class: ("is-active" unless params[:archived].present?) do
+ = link_to filter_groups_path(archived: nil), class: ("is-active" unless params[:archived].present?) do
Hide archived projects
%li.js-filter-archived-projects
- = link_to group_children_path(@group, archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
+ = link_to filter_groups_path(archived: true), class: ("is-active" if Gitlab::Utils.to_boolean(params[:archived])) do
Show archived projects
%li.js-filter-archived-projects
- = link_to group_children_path(@group, archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
+ = link_to filter_groups_path(archived: 'only'), class: ("is-active" if params[:archived] == 'only') do
Show archived projects only
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 79021a08719..6dfabd7ba4c 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -69,7 +69,7 @@
- else
= form.submit 'Save changes', class: 'btn btn-save'
- - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project))
+ - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = issuable.project.present.contribution_guide_path)
.inline.prepend-top-10
Please review the
%strong= link_to('contribution guidelines', guide_url)
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 15fd01c8429..dc583d3eb3b 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -1,7 +1,7 @@
- todo = issuable_todo(issuable)
- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('common_vue')
- = page_specific_javascript_bundle_tag('sidebar')
+ = webpack_bundle_tag('common_vue')
+ = webpack_bundle_tag('sidebar')
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: current_user.present? } }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar{ data: { endpoint: "#{issuable_json_path(issuable)}" } }
diff --git a/app/views/shared/members/_member.html.haml b/app/views/shared/members/_member.html.haml
index 71878e93255..ba57d922c6d 100644
--- a/app/views/shared/members/_member.html.haml
+++ b/app/views/shared/members/_member.html.haml
@@ -8,7 +8,7 @@
%li.member{ class: dom_class(member), id: dom_id(member) }
%span.list-item-name
- if user
- = image_tag avatar_icon(user, 40), class: "avatar s40", alt: ''
+ = image_tag avatar_icon_for_user(user, 40), class: "avatar s40", alt: ''
.user-info
= link_to user.name, user_path(user), class: 'member'
%span.cgray= user.to_reference
@@ -36,7 +36,7 @@
Expires in #{distance_of_time_in_words_to_now(member.expires_at)}
- else
- = image_tag avatar_icon(member.invite_email, 40), class: "avatar s40", alt: ''
+ = image_tag avatar_icon_for_email(member.invite_email, 40), class: "avatar s40", alt: ''
.user-info
.member= member.invite_email
.cgray
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 14395bcc661..129f6ab604e 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -17,7 +17,7 @@
= confidential_icon(issuable)
= link_to issuable.title, issuable_url_args, title: issuable.title
.issuable-detail
- = link_to [namespace, project, issuable] do
+ = link_to [namespace, project, issuable], class: 'issue-link' do
%span.issuable-number= issuable.to_reference
- labels.each do |label|
@@ -28,4 +28,4 @@
- assignees.each do |assignee|
= link_to polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, assignee_id: assignee.id, state: 'all' }),
class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
- - image_tag(avatar_icon(assignee, 16), class: "avatar s16", alt: '')
+ - image_tag(avatar_icon_for_user(assignee, 16), class: "avatar s16", alt: '')
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index e08a49b4e59..e3b2b53833e 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -51,7 +51,7 @@
\
- if @project.group
- = link_to promote_project_milestone_path(milestone.project, milestone), title: "Promote to Group Milestone", class: 'btn btn-xs btn-grouped', data: { confirm: "You are about to promote #{milestone.title} to a group level. This will make this milestone available to all projects inside #{@project.group.name}. The existing project milestone will be merged into the group level. This action cannot be reversed.", toggle: "tooltip" }, method: :post do
+ = link_to promote_project_milestone_path(milestone.project, milestone), title: "Promote to Group Milestone", class: 'btn btn-xs btn-grouped', data: { confirm: "Promoting #{milestone.title} will make it available for all projects inside #{@project.group.name}. Existing project milestones with the same name will be merged. This action cannot be reversed.", toggle: "tooltip" }, method: :post do
Promote
= link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped"
diff --git a/app/views/shared/milestones/_participants_tab.html.haml b/app/views/shared/milestones/_participants_tab.html.haml
index 1615871385e..fe83040c168 100644
--- a/app/views/shared/milestones/_participants_tab.html.haml
+++ b/app/views/shared/milestones/_participants_tab.html.haml
@@ -2,7 +2,7 @@
- users.each do |user|
%li
= link_to user, title: user.name, class: "darken" do
- = image_tag avatar_icon(user, 32), class: "avatar s32"
+ = image_tag avatar_icon_for_user(user, 32), class: "avatar s32"
%strong= truncate(user.name, length: 40)
%div
%small.cgray= user.username
diff --git a/app/views/shared/milestones/_sidebar.html.haml b/app/views/shared/milestones/_sidebar.html.haml
index 4f51455c26e..cd4188daf5b 100644
--- a/app/views/shared/milestones/_sidebar.html.haml
+++ b/app/views/shared/milestones/_sidebar.html.haml
@@ -1,5 +1,7 @@
- affix_offset = local_assigns.fetch(:affix_offset, "50")
- project = local_assigns[:project]
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('common_vue')
%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => affix_offset, "spy" => "affix", "always-show-toggle" => true }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar.milestone-sidebar
@@ -85,21 +87,12 @@
Closed:
= milestone.issues_visible_to_user(current_user).closed.count
- .block.time_spent
- .sidebar-collapsed-icon
- = custom_icon('icon_hourglass')
- %span.collapsed-milestone-total-time-spent
- - if milestone.human_total_issue_time_spent
- = milestone.human_total_issue_time_spent
- - else
- = _("None")
- .title.hide-collapsed
- = _("Total issue time spent")
- .value.hide-collapsed
- - if milestone.human_total_issue_time_spent
- %span.bold= milestone.human_total_issue_time_spent
- - else
- %span.no-value= _("No time spent")
+ .block
+ #issuable-time-tracker{ data: { time_estimate: @milestone.total_issue_time_estimate, time_spent: @milestone.total_issue_time_spent, human_time_estimate: @milestone.human_total_issue_time_estimate, human_time_spent: @milestone.human_total_issue_time_spent } }
+ // Fallback while content is loading
+ .title.hide-collapsed
+ = _('Time tracking')
+ = icon('spinner spin')
.block.merge-requests
.sidebar-collapsed-icon
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index 98e0161f7d1..bf359774ead 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -16,7 +16,7 @@
= icon_for_system_note(note)
- else
%a.image-diff-avatar-link{ href: user_path(note.author) }
- = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
+ = image_tag avatar_icon_for_user(note.author), alt: '', class: 'avatar s40'
- if note.is_a?(DiffNote) && note.on_image?
- if show_image_comment_badge && note_counter == 0
-# Only show this for the first comment in the discussion
diff --git a/app/views/shared/notes/_notes_with_form.html.haml b/app/views/shared/notes/_notes_with_form.html.haml
index e11f778adf5..b3f865c5b47 100644
--- a/app/views/shared/notes/_notes_with_form.html.haml
+++ b/app/views/shared/notes/_notes_with_form.html.haml
@@ -14,7 +14,7 @@
.timeline-icon.hidden-xs.hidden-sm
%a.author_link{ href: user_path(current_user) }
- = image_tag avatar_icon(current_user), alt: current_user.to_reference, class: 'avatar s40'
+ = image_tag avatar_icon_for_user(current_user), alt: current_user.to_reference, class: 'avatar s40'
.timeline-content.timeline-content-form
= render "shared/notes/form", view: diff_view, supports_autocomplete: autocomplete
- elsif !current_user
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index 2a75b46d376..0687f6d961d 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -6,7 +6,7 @@
- user = local_assigns[:user]
- access = user&.max_member_access_for_project(project.id) unless user.nil?
- css_class = '' unless local_assigns[:css_class]
-- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && project.commit
+- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && can_show_last_commit_in_list?(project)
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
- cache_key = project_list_cache_key(project)
- updated_tooltip = time_ago_with_tooltip(project.last_activity_date)
@@ -17,7 +17,7 @@
.avatar-container.s40
= link_to project_path(project), class: dom_class(project) do
- if project.creator && use_creator_avatar
- = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
+ = image_tag avatar_icon_for_user(project.creator, 40), class: "avatar s40", alt:''
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s40')
.project-details
@@ -47,7 +47,7 @@
.prepend-top-0
- if project.archived
%span.prepend-left-10.label.label-warning archived
- - if project.pipeline_status.has_status?
+ - if can?(current_user, :read_cross_project) && project.pipeline_status.has_status?
%span.prepend-left-10
= render_project_pipeline_status(project.pipeline_status)
- if forks
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 43322978749..2726a4934fb 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -1,6 +1,6 @@
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
- = page_specific_javascript_bundle_tag('snippet')
+ = webpack_bundle_tag('snippet')
.snippet-form-holder
= form_for @snippet, url: url, html: { class: "form-horizontal snippet-form js-requires-input js-quick-submit common-note-form" } do |f|
diff --git a/app/views/shared/snippets/_snippet.html.haml b/app/views/shared/snippets/_snippet.html.haml
index 57b445321e2..491a8a41090 100644
--- a/app/views/shared/snippets/_snippet.html.haml
+++ b/app/views/shared/snippets/_snippet.html.haml
@@ -1,7 +1,7 @@
- link_project = local_assigns.fetch(:link_project, false)
%li.snippet-row
- = image_tag avatar_icon(snippet.author), class: "avatar s40 hidden-xs", alt: ''
+ = image_tag avatar_icon_for_user(snippet.author), class: "avatar s40 hidden-xs", alt: ''
.title
= link_to reliable_snippet_path(snippet) do
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 90aa1be30ac..4bf01ecb48c 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -4,9 +4,6 @@
- page_description @user.bio
- header_title @user.name, user_path(@user)
- @no_container = true
-- content_for :page_specific_javascripts do
- = webpack_bundle_tag 'common_d3'
- = webpack_bundle_tag 'users'
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
@@ -35,8 +32,8 @@
.profile-header
.avatar-holder
- = link_to avatar_icon(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
- = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
+ = link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
+ = image_tag avatar_icon_for_user(@user, 90), class: "avatar s90", alt: ''
.user-info
.cover-title
@@ -85,47 +82,58 @@
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
%ul.nav-links.user-profile-nav.scrolling-tabs
- %li.js-activity-tab
- = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
- Activity
- %li.js-groups-tab
- = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
- Groups
- %li.js-contributed-tab
- = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
- Contributed projects
- %li.js-projects-tab
- = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
- Personal projects
- %li.js-snippets-tab
- = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
- Snippets
+ - if profile_tab?(:activity)
+ %li.js-activity-tab
+ = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do
+ Activity
+ - if profile_tab?(:groups)
+ %li.js-groups-tab
+ = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do
+ Groups
+ - if profile_tab?(:contributed)
+ %li.js-contributed-tab
+ = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do
+ Contributed projects
+ - if profile_tab?(:projects)
+ %li.js-projects-tab
+ = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do
+ Personal projects
+ - if profile_tab?(:snippets)
+ %li.js-snippets-tab
+ = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do
+ Snippets
%div{ class: container_class }
.tab-content
- #activity.tab-pane
- .row-content-block.calender-block.white.second-block.hidden-xs
- .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
- %h4.center.light
- %i.fa.fa-spinner.fa-spin
- .user-calendar-activities
+ - if profile_tab?(:activity)
+ #activity.tab-pane
+ .row-content-block.calender-block.white.second-block.hidden-xs
+ .user-calendar{ data: { calendar_path: user_calendar_path(@user, :json), calendar_activities_path: user_calendar_activities_path, utc_offset: Time.zone.utc_offset } }
+ %h4.center.light
+ %i.fa.fa-spinner.fa-spin
+ .user-calendar-activities
- %h4.prepend-top-20
- Most Recent Activity
- .content_list{ data: { href: user_path } }
- = spinner
+ - if can?(current_user, :read_cross_project)
+ %h4.prepend-top-20
+ Most Recent Activity
+ .content_list{ data: { href: user_path } }
+ = spinner
- #groups.tab-pane
- -# This tab is always loaded via AJAX
+ - if profile_tab?(:groups)
+ #groups.tab-pane
+ -# This tab is always loaded via AJAX
- #contributed.tab-pane
- -# This tab is always loaded via AJAX
+ - if profile_tab?(:contributed)
+ #contributed.tab-pane
+ -# This tab is always loaded via AJAX
- #projects.tab-pane
- -# This tab is always loaded via AJAX
+ - if profile_tab?(:projects)
+ #projects.tab-pane
+ -# This tab is always loaded via AJAX
- #snippets.tab-pane
- -# This tab is always loaded via AJAX
+ - if profile_tab?(:snippets)
+ #snippets.tab-pane
+ -# This tab is always loaded via AJAX
.loading-status
= spinner
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index f2c20114534..28a5e5da037 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -3,6 +3,7 @@
- cronjob:expire_build_artifacts
- cronjob:gitlab_usage_ping
- cronjob:import_export_project_cleanup
+- cronjob:pages_domain_verification_cron
- cronjob:pipeline_schedule
- cronjob:prune_old_events
- cronjob:remove_expired_group_links
@@ -82,6 +83,7 @@
- new_merge_request
- new_note
- pages
+- pages_domain_verification
- post_receive
- process_commit
- project_cache
diff --git a/app/workers/authorized_projects_worker.rb b/app/workers/authorized_projects_worker.rb
index 09559e3b696..d7e24491516 100644
--- a/app/workers/authorized_projects_worker.rb
+++ b/app/workers/authorized_projects_worker.rb
@@ -1,42 +1,10 @@
class AuthorizedProjectsWorker
include ApplicationWorker
+ prepend WaitableWorker
- # Schedules multiple jobs and waits for them to be completed.
- def self.bulk_perform_and_wait(args_list)
- # Short-circuit: it's more efficient to do small numbers of jobs inline
- return bulk_perform_inline(args_list) if args_list.size <= 3
-
- waiter = Gitlab::JobWaiter.new(args_list.size)
-
- # Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]]
- # into [[1, "key"], [2, "key"], [3, "key"]]
- waiting_args_list = args_list.map { |args| [*args, waiter.key] }
- bulk_perform_async(waiting_args_list)
-
- waiter.wait
- end
-
- # Performs multiple jobs directly. Failed jobs will be put into sidekiq so
- # they can benefit from retries
- def self.bulk_perform_inline(args_list)
- failed = []
-
- args_list.each do |args|
- begin
- new.perform(*args)
- rescue
- failed << args
- end
- end
-
- bulk_perform_async(failed) if failed.present?
- end
-
- def perform(user_id, notify_key = nil)
+ def perform(user_id)
user = User.find_by(id: user_id)
user&.refresh_authorized_projects
- ensure
- Gitlab::JobWaiter.notify(notify_key, jid) if notify_key
end
end
diff --git a/app/workers/check_gcp_project_billing_worker.rb b/app/workers/check_gcp_project_billing_worker.rb
index 5466ccdda59..363f81590ab 100644
--- a/app/workers/check_gcp_project_billing_worker.rb
+++ b/app/workers/check_gcp_project_billing_worker.rb
@@ -7,6 +7,7 @@ class CheckGcpProjectBillingWorker
LEASE_TIMEOUT = 3.seconds.to_i
SESSION_KEY_TIMEOUT = 5.minutes
BILLING_TIMEOUT = 1.hour
+ BILLING_CHANGED_LABELS = { state_transition: nil }.freeze
def self.get_session_token(token_key)
Gitlab::Redis::SharedState.with do |redis|
@@ -22,8 +23,11 @@ class CheckGcpProjectBillingWorker
end
end
- def self.redis_shared_state_key_for(token)
- "gitlab:gcp:#{Digest::SHA1.hexdigest(token)}:billing_enabled"
+ def self.get_billing_state(token)
+ Gitlab::Redis::SharedState.with do |redis|
+ value = redis.get(redis_shared_state_key_for(token))
+ ActiveRecord::Type::Boolean.new.type_cast_from_user(value)
+ end
end
def perform(token_key)
@@ -33,12 +37,9 @@ class CheckGcpProjectBillingWorker
return unless token
return unless try_obtain_lease_for(token)
- billing_enabled_projects = CheckGcpProjectBillingService.new.execute(token)
- Gitlab::Redis::SharedState.with do |redis|
- redis.set(self.class.redis_shared_state_key_for(token),
- !billing_enabled_projects.empty?,
- ex: BILLING_TIMEOUT)
- end
+ billing_enabled_state = !CheckGcpProjectBillingService.new.execute(token).empty?
+ update_billing_change_counter(self.class.get_billing_state(token), billing_enabled_state)
+ self.class.set_billing_state(token, billing_enabled_state)
end
private
@@ -51,9 +52,41 @@ class CheckGcpProjectBillingWorker
"gitlab:gcp:session:#{token_key}"
end
+ def self.redis_shared_state_key_for(token)
+ "gitlab:gcp:#{Digest::SHA1.hexdigest(token)}:billing_enabled"
+ end
+
+ def self.set_billing_state(token, value)
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set(redis_shared_state_key_for(token), value, ex: BILLING_TIMEOUT)
+ end
+ end
+
def try_obtain_lease_for(token)
Gitlab::ExclusiveLease
.new("check_gcp_project_billing_worker:#{token.hash}", timeout: LEASE_TIMEOUT)
.try_obtain
end
+
+ def billing_changed_counter
+ @billing_changed_counter ||= Gitlab::Metrics.counter(
+ :gcp_billing_change_count,
+ "Counts the number of times a GCP project changed billing_enabled state from false to true",
+ BILLING_CHANGED_LABELS
+ )
+ end
+
+ def state_transition(previous_state, current_state)
+ if previous_state.nil? && !current_state
+ 'no_billing'
+ elsif previous_state.nil? && current_state
+ 'with_billing'
+ elsif !previous_state && current_state
+ 'billing_configured'
+ end
+ end
+
+ def update_billing_change_counter(previous_state, current_state)
+ billing_changed_counter.increment(state_transition: state_transition(previous_state, current_state))
+ end
end
diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb
new file mode 100644
index 00000000000..48ebe862248
--- /dev/null
+++ b/app/workers/concerns/waitable_worker.rb
@@ -0,0 +1,44 @@
+module WaitableWorker
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Schedules multiple jobs and waits for them to be completed.
+ def bulk_perform_and_wait(args_list, timeout: 10)
+ # Short-circuit: it's more efficient to do small numbers of jobs inline
+ return bulk_perform_inline(args_list) if args_list.size <= 3
+
+ waiter = Gitlab::JobWaiter.new(args_list.size)
+
+ # Point all the bulk jobs at the same JobWaiter. Converts, [[1], [2], [3]]
+ # into [[1, "key"], [2, "key"], [3, "key"]]
+ waiting_args_list = args_list.map { |args| [*args, waiter.key] }
+ bulk_perform_async(waiting_args_list)
+
+ waiter.wait(timeout)
+ end
+
+ # Performs multiple jobs directly. Failed jobs will be put into sidekiq so
+ # they can benefit from retries
+ def bulk_perform_inline(args_list)
+ failed = []
+
+ args_list.each do |args|
+ begin
+ new.perform(*args)
+ rescue
+ failed << args
+ end
+ end
+
+ bulk_perform_async(failed) if failed.present?
+ end
+ end
+
+ def perform(*args)
+ notify_key = args.pop if Gitlab::JobWaiter.key?(args.last)
+
+ super(*args)
+ ensure
+ Gitlab::JobWaiter.notify(notify_key, jid) if notify_key
+ end
+end
diff --git a/app/workers/pages_domain_verification_cron_worker.rb b/app/workers/pages_domain_verification_cron_worker.rb
new file mode 100644
index 00000000000..a3ff4bd2101
--- /dev/null
+++ b/app/workers/pages_domain_verification_cron_worker.rb
@@ -0,0 +1,10 @@
+class PagesDomainVerificationCronWorker
+ include ApplicationWorker
+ include CronjobQueue
+
+ def perform
+ PagesDomain.needs_verification.find_each do |domain|
+ PagesDomainVerificationWorker.perform_async(domain.id)
+ end
+ end
+end
diff --git a/app/workers/pages_domain_verification_worker.rb b/app/workers/pages_domain_verification_worker.rb
new file mode 100644
index 00000000000..2e93489113c
--- /dev/null
+++ b/app/workers/pages_domain_verification_worker.rb
@@ -0,0 +1,11 @@
+class PagesDomainVerificationWorker
+ include ApplicationWorker
+
+ def perform(domain_id)
+ domain = PagesDomain.find_by(id: domain_id)
+
+ return unless domain
+
+ VerifyPagesDomainService.new(domain).execute
+ end
+end
diff --git a/app/workers/process_commit_worker.rb b/app/workers/process_commit_worker.rb
index 52eebe475ec..5b25d980bdb 100644
--- a/app/workers/process_commit_worker.rb
+++ b/app/workers/process_commit_worker.rb
@@ -23,27 +23,25 @@ class ProcessCommitWorker
return unless user
commit = build_commit(project, commit_hash)
-
author = commit.author || user
process_commit_message(project, commit, user, author, default)
-
update_issue_metrics(commit, author)
end
def process_commit_message(project, commit, user, author, default = false)
- closed_issues = default ? commit.closes_issues(user) : []
+ # this is a GitLab generated commit message, ignore it.
+ return if commit.merged_merge_request?(user)
- unless closed_issues.empty?
- close_issues(project, user, author, commit, closed_issues)
- end
+ closed_issues = default ? commit.closes_issues(user) : []
+ close_issues(project, user, author, commit, closed_issues) if closed_issues.any?
commit.create_cross_references!(author, closed_issues)
end
def close_issues(project, user, author, commit, issues)
# We don't want to run permission related queries for every single issue,
- # therefor we use IssueCollection here and skip the authorization check in
+ # therefore we use IssueCollection here and skip the authorization check in
# Issues::CloseService#execute.
IssueCollection.new(issues).updatable_by_user(user).each do |issue|
Issues::CloseService.new(project, author)
diff --git a/app/workers/stuck_import_jobs_worker.rb b/app/workers/stuck_import_jobs_worker.rb
index e0e6d1418de..fbb14efc525 100644
--- a/app/workers/stuck_import_jobs_worker.rb
+++ b/app/workers/stuck_import_jobs_worker.rb
@@ -16,43 +16,41 @@ class StuckImportJobsWorker
private
def mark_projects_without_jid_as_failed!
- started_projects_without_jid.each do |project|
+ enqueued_projects_without_jid.each do |project|
project.mark_import_as_failed(error_message)
end.count
end
def mark_projects_with_jid_as_failed!
- completed_jids_count = 0
+ jids_and_ids = enqueued_projects_with_jid.pluck(:import_jid, :id).to_h
- started_projects_with_jid.find_in_batches(batch_size: 500) do |group|
- jids = group.map(&:import_jid)
+ # Find the jobs that aren't currently running or that exceeded the threshold.
+ completed_jids = Gitlab::SidekiqStatus.completed_jids(jids_and_ids.keys)
+ return unless completed_jids.any?
- # Find the jobs that aren't currently running or that exceeded the threshold.
- completed_jids = Gitlab::SidekiqStatus.completed_jids(jids).to_set
+ completed_project_ids = jids_and_ids.values_at(*completed_jids)
- if completed_jids.any?
- completed_jids_count += completed_jids.count
- group.each do |project|
- project.mark_import_as_failed(error_message) if completed_jids.include?(project.import_jid)
- end
+ # We select the projects again, because they may have transitioned from
+ # scheduled/started to finished/failed while we were looking up their Sidekiq status.
+ completed_projects = enqueued_projects_with_jid.where(id: completed_project_ids)
- Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_jids.to_a.join(', ')}")
- end
- end
+ Rails.logger.info("Marked stuck import jobs as failed. JIDs: #{completed_projects.map(&:import_jid).join(', ')}")
- completed_jids_count
+ completed_projects.each do |project|
+ project.mark_import_as_failed(error_message)
+ end.count
end
- def started_projects
- Project.with_import_status(:started)
+ def enqueued_projects
+ Project.with_import_status(:scheduled, :started)
end
- def started_projects_with_jid
- started_projects.where.not(import_jid: nil)
+ def enqueued_projects_with_jid
+ enqueued_projects.where.not(import_jid: nil)
end
- def started_projects_without_jid
- started_projects.where(import_jid: nil)
+ def enqueued_projects_without_jid
+ enqueued_projects.where(import_jid: nil)
end
def error_message
diff --git a/bin/changelog b/bin/changelog
index efe25032ba1..9b60f53ce40 100755
--- a/bin/changelog
+++ b/bin/changelog
@@ -214,9 +214,10 @@ class ChangelogEntry
end
def unreleased_path
- File.join('changelogs', 'unreleased').tap do |path|
- path << '-ee' if ee?
- end
+ path = File.join('changelogs', 'unreleased')
+ path = File.join('ee', path) if ee?
+
+ path
end
def ee?
diff --git a/changelogs/unreleased-ee/bvl-external-policy-classification.yml b/changelogs/unreleased-ee/bvl-external-policy-classification.yml
new file mode 100644
index 00000000000..074629c8c12
--- /dev/null
+++ b/changelogs/unreleased-ee/bvl-external-policy-classification.yml
@@ -0,0 +1,5 @@
+---
+title: Authorize project access with an external service
+merge_request: 4675
+author:
+type: added
diff --git a/changelogs/unreleased/14256-upload-destroy-removes-file.yml b/changelogs/unreleased/14256-upload-destroy-removes-file.yml
deleted file mode 100644
index d97188e23f1..00000000000
--- a/changelogs/unreleased/14256-upload-destroy-removes-file.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Deleting an upload will correctly clean up the filesystem.
-merge_request: 16799
-author:
-type: fixed
diff --git a/changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml b/changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml
deleted file mode 100644
index e94b4f8bb26..00000000000
--- a/changelogs/unreleased/16301-update-removed-assignee-note-to-include-old-assignee-reference.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Update 'removed assignee' note to include old assignee reference"
-merge_request: 16301
-author: Maurizio De Santis
-type: changed
diff --git a/changelogs/unreleased/16468-add-fast-blank.yml b/changelogs/unreleased/16468-add-fast-blank.yml
deleted file mode 100644
index ef68888ae33..00000000000
--- a/changelogs/unreleased/16468-add-fast-blank.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Add fast-blank"
-merge_request: 16468
-author:
-type: performance
diff --git a/changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml b/changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml
new file mode 100644
index 00000000000..efd936ca104
--- /dev/null
+++ b/changelogs/unreleased/17203-add-missing-pagination-commit-diff-endpoint.yml
@@ -0,0 +1,5 @@
+---
+ title: Add missing pagination on the commit diff endpoint
+ merge_request: 17203
+ author: Maxime Roussin-Bélanger
+ type: fixed
diff --git a/changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml b/changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml
new file mode 100644
index 00000000000..a94e6153a05
--- /dev/null
+++ b/changelogs/unreleased/17500-mr-multiple-issues-oxford-comma.yml
@@ -0,0 +1,5 @@
+---
+title: Update issue closing pattern to allow variations in punctuation
+merge_request: 17198
+author: Vicky Chijwani
+type: changed
diff --git a/changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml b/changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml
deleted file mode 100644
index 447c65a3764..00000000000
--- a/changelogs/unreleased/18040-line-breaks-around-conditional-blocks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds Rubocop rule for line break around conditionals
-merge_request: 15739
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml b/changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml
deleted file mode 100644
index 962f918e9db..00000000000
--- a/changelogs/unreleased/19493-fork-does-not-protect-default-branch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Makes forking protect default branch on completion
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/21554-mark-new-user-as-external.yml b/changelogs/unreleased/21554-mark-new-user-as-external.yml
deleted file mode 100644
index fb0826fc176..00000000000
--- a/changelogs/unreleased/21554-mark-new-user-as-external.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Login via OAuth now only marks new users as external
-merge_request: 16672
-author:
-type: fixed
diff --git a/changelogs/unreleased/24035-api-create-application.yml b/changelogs/unreleased/24035-api-create-application.yml
deleted file mode 100644
index c583a020d9d..00000000000
--- a/changelogs/unreleased/24035-api-create-application.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add application create API
-merge_request: 8160
-author: Nicolas Merelli @PNSalocin
diff --git a/changelogs/unreleased/24167__color_label.yml b/changelogs/unreleased/24167__color_label.yml
deleted file mode 100644
index 68c6c731163..00000000000
--- a/changelogs/unreleased/24167__color_label.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Colors to GitLab Flavored Markdown
-merge_request: 16095
-author: Tony Rom <thetonyrom@gmail.com>
-type: added
diff --git a/changelogs/unreleased/25327-coverage-badge-rounding.yml b/changelogs/unreleased/25327-coverage-badge-rounding.yml
deleted file mode 100644
index ea985689484..00000000000
--- a/changelogs/unreleased/25327-coverage-badge-rounding.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show coverage to two decimal points in coverage badge
-merge_request: 10083
-author: Jeff Stubler
-type: changed
diff --git a/changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml b/changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml
new file mode 100644
index 00000000000..0f1cb2fef9d
--- /dev/null
+++ b/changelogs/unreleased/26039-Update-to-github-linguist5-3-x.yml
@@ -0,0 +1,5 @@
+---
+title: Update to github-linguist 5.3.x
+merge_request: 17241
+author: Ken Ding
+type: other
diff --git a/changelogs/unreleased/26296-update-styling-disabled-buttons.yml b/changelogs/unreleased/26296-update-styling-disabled-buttons.yml
deleted file mode 100644
index 5fa109d75e0..00000000000
--- a/changelogs/unreleased/26296-update-styling-disabled-buttons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Set standard disabled state for all buttons
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/26388-push-to-create-a-new-project.yml b/changelogs/unreleased/26388-push-to-create-a-new-project.yml
deleted file mode 100644
index f641fcced37..00000000000
--- a/changelogs/unreleased/26388-push-to-create-a-new-project.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: User can now git push to create a new project
-merge_request: 16547
-author:
-type: added
diff --git a/changelogs/unreleased/26466-natural-sort-mrs.yml b/changelogs/unreleased/26466-natural-sort-mrs.yml
new file mode 100644
index 00000000000..e3bf9834f24
--- /dev/null
+++ b/changelogs/unreleased/26466-natural-sort-mrs.yml
@@ -0,0 +1,4 @@
+---
+title: Group MRs on issue page by project and namespace.
+merge_request: 8494
+author: Jeff Stubler
diff --git a/changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml b/changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml
deleted file mode 100644
index a2c81f6c995..00000000000
--- a/changelogs/unreleased/26468-fix-sort-by-recent-sign-in.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix Sort by Recent Sign-in in Admin Area
-merge_request: 13852
-author: Poornima M
diff --git a/changelogs/unreleased/28260-fix-pages-custom-domain-url.yml b/changelogs/unreleased/28260-fix-pages-custom-domain-url.yml
deleted file mode 100644
index edd63c4ea9c..00000000000
--- a/changelogs/unreleased/28260-fix-pages-custom-domain-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Generate HTTP URLs for custom Pages domains when appropriate
-merge_request: 16279
-author:
-type: fixed
diff --git a/changelogs/unreleased/29497-pages-custom-domain-dns-verification.yml b/changelogs/unreleased/29497-pages-custom-domain-dns-verification.yml
new file mode 100644
index 00000000000..f958f3f1272
--- /dev/null
+++ b/changelogs/unreleased/29497-pages-custom-domain-dns-verification.yml
@@ -0,0 +1,5 @@
+---
+title: Add verification for GitLab Pages custom domains
+merge_request:
+author:
+type: security
diff --git a/changelogs/unreleased/30106-group-issues.yml b/changelogs/unreleased/30106-group-issues.yml
deleted file mode 100644
index d24996e6087..00000000000
--- a/changelogs/unreleased/30106-group-issues.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include subgroup issues and merge requests on the group page
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml b/changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml
deleted file mode 100644
index d2a5802af64..00000000000
--- a/changelogs/unreleased/31885-ability-to-transfer-groups-to-another-group.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add ability to transfer a group into another group
-merge_request: 16302
-author:
-type: added
diff --git a/changelogs/unreleased/32282-add-foreign-keys-to-todos.yml b/changelogs/unreleased/32282-add-foreign-keys-to-todos.yml
deleted file mode 100644
index e74c2a8b9ff..00000000000
--- a/changelogs/unreleased/32282-add-foreign-keys-to-todos.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add foreign key and NOT NULL constraints to todos table.
-merge_request: 16849
-author:
-type: other
diff --git a/changelogs/unreleased/32283-trending-projects-unique-constraint2.yml b/changelogs/unreleased/32283-trending-projects-unique-constraint2.yml
deleted file mode 100644
index 4fd6b6fddc4..00000000000
--- a/changelogs/unreleased/32283-trending-projects-unique-constraint2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add unique constraint to trending_projects#project_id.
-merge_request: 16846
-author:
-type: other
diff --git a/changelogs/unreleased/32564-fix-double-system-closing-notes.yml b/changelogs/unreleased/32564-fix-double-system-closing-notes.yml
new file mode 100644
index 00000000000..e6e1ef8c76d
--- /dev/null
+++ b/changelogs/unreleased/32564-fix-double-system-closing-notes.yml
@@ -0,0 +1,5 @@
+---
+title: Fix duplicate system notes when merging a merge request.
+merge_request: 17035
+author:
+type: fixed
diff --git a/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml b/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml
deleted file mode 100644
index 09e2af1e4d3..00000000000
--- a/changelogs/unreleased/34055-issues-enabled-filter-misbehavior.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix the Projects API with_issues_enabled filter behaving incorrectly
- any user
-merge_request: 12724
-author: Jan Christophersen
-type: fixed
diff --git a/changelogs/unreleased/34252-trailing-plus.yml b/changelogs/unreleased/34252-trailing-plus.yml
deleted file mode 100644
index fce17cb6ab9..00000000000
--- a/changelogs/unreleased/34252-trailing-plus.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow trailing + on labels in board filters
-merge_request: 16490
-author:
-type: fixed
diff --git a/changelogs/unreleased/34416-issue-i18n.yml b/changelogs/unreleased/34416-issue-i18n.yml
deleted file mode 100644
index 523073ee43b..00000000000
--- a/changelogs/unreleased/34416-issue-i18n.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Translate issuable sidebar
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml b/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml
deleted file mode 100644
index 0791847b64d..00000000000
--- a/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix default avatar icon missing when Gravatar is disabled
-merge_request: 16681
-author: Felix Geyer
-type: fixed
diff --git a/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml b/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml
deleted file mode 100644
index f3a04469884..00000000000
--- a/changelogs/unreleased/35285-user-interface-bugs-for-schedule-pipelines.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide pipeline schedule take ownership for current owner
-merge_request: 12986
-author:
-type: fixed
diff --git a/changelogs/unreleased/35418-remove-underline-for-avatar.yml b/changelogs/unreleased/35418-remove-underline-for-avatar.yml
new file mode 100644
index 00000000000..034365e1137
--- /dev/null
+++ b/changelogs/unreleased/35418-remove-underline-for-avatar.yml
@@ -0,0 +1,5 @@
+---
+title: remove avater underline
+merge_request: 17219
+author: Ken Ding
+type: fixed
diff --git a/changelogs/unreleased/35530-teleporting-emoji.yml b/changelogs/unreleased/35530-teleporting-emoji.yml
new file mode 100644
index 00000000000..a60a42b9e48
--- /dev/null
+++ b/changelogs/unreleased/35530-teleporting-emoji.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Teleporting Emoji
+merge_request: 16963
+author: Jared Deckard <jared.deckard@gmail.com>
+type: fixed
diff --git a/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml b/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml
deleted file mode 100644
index 82df00fe631..00000000000
--- a/changelogs/unreleased/35779-realtime-update-of-pipeline-status-in-files-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add realtime ci status for the repository -> files view
-merge_request: 16523
-author:
-type: added
diff --git a/changelogs/unreleased/35856-implement-file-locking-api.yml b/changelogs/unreleased/35856-implement-file-locking-api.yml
deleted file mode 100644
index fa848ad9ed8..00000000000
--- a/changelogs/unreleased/35856-implement-file-locking-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Backport of LFS File Locking API
-merge_request: 16935
-author:
-type: added
diff --git a/changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml b/changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml
new file mode 100644
index 00000000000..74eaf57c056
--- /dev/null
+++ b/changelogs/unreleased/36847-update-update-toml-rb-to-1-0-0.yml
@@ -0,0 +1,5 @@
+---
+title: update toml-rb to 1.0.0
+merge_request: 17259
+author: Ken Ding
+type: other
diff --git a/changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml b/changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml
deleted file mode 100644
index 0ab765a43b7..00000000000
--- a/changelogs/unreleased/36906-reordering-issues-to-the-bottom.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Issue board: fix for dragging an issue to the very bottom in long lists"
-merge_request: 16250
-author: David Kuri
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/37050-ext-issue-tracker.yml b/changelogs/unreleased/37050-ext-issue-tracker.yml
new file mode 100644
index 00000000000..29bccdded02
--- /dev/null
+++ b/changelogs/unreleased/37050-ext-issue-tracker.yml
@@ -0,0 +1,5 @@
+---
+title: Display a link to external issue tracker when enabled
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/37199-labels-fix.yml b/changelogs/unreleased/37199-labels-fix.yml
deleted file mode 100644
index bd70babb73d..00000000000
--- a/changelogs/unreleased/37199-labels-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Keep subscribers when promoting labels to group labels
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml b/changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml
deleted file mode 100644
index 813b9ab81fa..00000000000
--- a/changelogs/unreleased/37898-increase-readability-of-colored-text-in-job-output-log.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: increase-readability-of-colored-text-in-job-output-log
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/38068-commits-count.yml b/changelogs/unreleased/38068-commits-count.yml
deleted file mode 100644
index 3fbf554c98c..00000000000
--- a/changelogs/unreleased/38068-commits-count.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Store number of commits in merge_request_diffs table.
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml b/changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml
deleted file mode 100644
index 475e1dc12b5..00000000000
--- a/changelogs/unreleased/38175-add-domain-field-to-auto-devops-application-setting.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Auto DevOps Domain application setting
-merge_request: 16604
-author:
-type: changed
diff --git a/changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml b/changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml
deleted file mode 100644
index 4d8e6acfcb7..00000000000
--- a/changelogs/unreleased/38265-stuckcijobsworker-wrongly-detects-cancels-stuck-builds-when-per-job-timeout-is-more-than-an-hour.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update runner info on all authenticated requests
-merge_request: 16756
-author:
-type: changed
diff --git a/changelogs/unreleased/38540-ssh-env-file.yml b/changelogs/unreleased/38540-ssh-env-file.yml
deleted file mode 100644
index 5ada0ede76d..00000000000
--- a/changelogs/unreleased/38540-ssh-env-file.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Closes #38540 - Remove .ssh/environment file that now breaks the gitlab:check
- rake task'
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/39214__pipeline_api.yml b/changelogs/unreleased/39214__pipeline_api.yml
deleted file mode 100644
index 18ee2e43798..00000000000
--- a/changelogs/unreleased/39214__pipeline_api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add `pipelines` endpoint to merge requests API
-merge_request: 15454
-author: Tony Rom <thetonyrom@gmail.com>
-type: added
diff --git a/changelogs/unreleased/39607-fix-avatar--vertical-align.yml b/changelogs/unreleased/39607-fix-avatar--vertical-align.yml
new file mode 100644
index 00000000000..4d9fee12f04
--- /dev/null
+++ b/changelogs/unreleased/39607-fix-avatar--vertical-align.yml
@@ -0,0 +1,5 @@
+---
+title: "Fix user avatar's vertical align on the issues and merge requests pages"
+merge_request: 17072
+author: Laszlo Karpati
+type: fixed
diff --git a/changelogs/unreleased/39917-revert-this-merge-request-text.yml b/changelogs/unreleased/39917-revert-this-merge-request-text.yml
deleted file mode 100644
index 9a27be1f9c6..00000000000
--- a/changelogs/unreleased/39917-revert-this-merge-request-text.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Changes Revert this merge request text
-merge_request: 16611
-author: Jacopo Beschi @jacopo-beschi
-type: changed
diff --git a/changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml b/changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml
deleted file mode 100644
index 5c45d0db602..00000000000
--- a/changelogs/unreleased/39985-enable-prometheus-metrics-for-deployed-ingresses.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable Prometheus metrics for deployed Ingresses
-merge_request: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/16866
-author: joshlambert
-type: changed
diff --git a/changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml b/changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml
deleted file mode 100644
index 4f2c87c44b3..00000000000
--- a/changelogs/unreleased/39988-hide-new-branch-tag-empty-repo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide new branch and tag links for projects with an empty repo
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml b/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml
deleted file mode 100644
index ffab28acbd5..00000000000
--- a/changelogs/unreleased/40028-special-characters-on-issuable-templates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Handle special characters on API request of issuable templates
-merge_request: 15323
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml b/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml
deleted file mode 100644
index 519f411d642..00000000000
--- a/changelogs/unreleased/40029-better-error-handling-on-issuable-templates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Stop loading spinner on error of issuable templates
-merge_request: 16600
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/4020-rebase-message.yml b/changelogs/unreleased/4020-rebase-message.yml
deleted file mode 100644
index 4793f3d9cb9..00000000000
--- a/changelogs/unreleased/4020-rebase-message.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display user friendly error message if rebase fails.
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/40492-update-admin-dashboard-content-order.yml b/changelogs/unreleased/40492-update-admin-dashboard-content-order.yml
deleted file mode 100644
index 2416b15b6d5..00000000000
--- a/changelogs/unreleased/40492-update-admin-dashboard-content-order.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move row containing Projects, Users and Groups count to the top in admin dashboard
-merge_request: 16421
-author:
-type: changed
diff --git a/changelogs/unreleased/40540-use-limit-for-global-search.yml b/changelogs/unreleased/40540-use-limit-for-global-search.yml
deleted file mode 100644
index 7d9612c16df..00000000000
--- a/changelogs/unreleased/40540-use-limit-for-global-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Optimize search queries on the search page by setting a limit for matching records.
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/40623-fix-404-when-listing-archived-projects-in-a-group-where-all-projects-have-been-archived.yml b/changelogs/unreleased/40623-fix-404-when-listing-archived-projects-in-a-group-where-all-projects-have-been-archived.yml
new file mode 100644
index 00000000000..543fd7c5e8d
--- /dev/null
+++ b/changelogs/unreleased/40623-fix-404-when-listing-archived-projects-in-a-group-where-all-projects-have-been-archived.yml
@@ -0,0 +1,4 @@
+title: Fix 404 when listing archived projects in a group where all projects have been archived
+merge_request: 17077
+author: Ashley Dumaine
+type: fixed
diff --git a/changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml b/changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml
new file mode 100644
index 00000000000..d77572d6175
--- /dev/null
+++ b/changelogs/unreleased/40668-pages-domain-api-returns-404-when-using-a-specific-domain.yml
@@ -0,0 +1,5 @@
+---
+title: Fix get a single pages domain when project path contains a period
+merge_request: 17206
+author: Travis Miller
+type: fixed
diff --git a/changelogs/unreleased/40755-snippets-author-n-1.yml b/changelogs/unreleased/40755-snippets-author-n-1.yml
deleted file mode 100644
index 6e09c8a54ec..00000000000
--- a/changelogs/unreleased/40755-snippets-author-n-1.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix N+1 query problem for snippets dashboard.
-merge_request: 16944
-author:
-type: performance
diff --git a/changelogs/unreleased/40793-fix-mr-title-for-jira.yml b/changelogs/unreleased/40793-fix-mr-title-for-jira.yml
deleted file mode 100644
index 69461510a87..00000000000
--- a/changelogs/unreleased/40793-fix-mr-title-for-jira.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent JIRA issue identifier from being humanized.
-merge_request: 16491
-author: Andrew McCallum
-type: fixed
diff --git a/changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml b/changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml
deleted file mode 100644
index c57caf31d10..00000000000
--- a/changelogs/unreleased/40818-last-push-widget-does-not-appear-after-pushing-new-commit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Last push widget will show banner for new pushes to previously merged branch
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/40994-expose-features-as-ci-cd-variable.yml b/changelogs/unreleased/40994-expose-features-as-ci-cd-variable.yml
new file mode 100644
index 00000000000..1e377094791
--- /dev/null
+++ b/changelogs/unreleased/40994-expose-features-as-ci-cd-variable.yml
@@ -0,0 +1,5 @@
+---
+title: 'Expose GITLAB_FEATURES as CI/CD variable (fixes #40994)'
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/41118-add-sorting-to-deployments-api.yml b/changelogs/unreleased/41118-add-sorting-to-deployments-api.yml
deleted file mode 100644
index a08f75f9fb9..00000000000
--- a/changelogs/unreleased/41118-add-sorting-to-deployments-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds sorting to deployments API
-merge_request: !16396
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml b/changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml
deleted file mode 100644
index 9c48831855c..00000000000
--- a/changelogs/unreleased/41163-improve-cluster-ingress-extra-cost-language.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve wording about additional costs for Ingress on custom clusters
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml b/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml
deleted file mode 100644
index 5e706740962..00000000000
--- a/changelogs/unreleased/41206-show-signin-pane-after-email-confirmation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Shows signin tab after new user email confirmation
-merge_request: 16174
-author: Jacopo Beschi @jacopo-beschi
-type: fixed
diff --git a/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml b/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml
deleted file mode 100644
index 76d3c6eda24..00000000000
--- a/changelogs/unreleased/41208-commit-atom-feeds-double-escaped.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allows html text in commits atom feed
-merge_request: 16603
-author: Jacopo Beschi @jacopo-beschi
-type: fixed
diff --git a/changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml b/changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml
deleted file mode 100644
index 61d6bf8fd36..00000000000
--- a/changelogs/unreleased/41209-ci-linter-fails-on-gitlab-ci-blob-viewer.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Handle all Psych YAML parser exceptions (fixes #41209)'
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/41247-timestamp.yml b/changelogs/unreleased/41247-timestamp.yml
deleted file mode 100644
index 65f1a7485ad..00000000000
--- a/changelogs/unreleased/41247-timestamp.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: For issues display time of last edit of title or description instead of time
- of any attribute change
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml b/changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml
deleted file mode 100644
index bb5c1fdf082..00000000000
--- a/changelogs/unreleased/41476-enable-project-milestons-deletion-via-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enables Project Milestone Deletion via the API
-merge_request: 16478
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/41532-email-reason.yml b/changelogs/unreleased/41532-email-reason.yml
deleted file mode 100644
index 83c28769217..00000000000
--- a/changelogs/unreleased/41532-email-reason.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Initial work to add notification reason to emails
-merge_request: 16160
-author: Mario de la Ossa
-type: added
diff --git a/changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml b/changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml
deleted file mode 100644
index 7e42dc20ae8..00000000000
--- a/changelogs/unreleased/41546-count-query-for-issues-and-mrs-runs-twice-on-group-index.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix double query execution on groups page
-merge_request: 16314
-author:
-type: performance
diff --git a/changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml b/changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml
deleted file mode 100644
index e50f6046b17..00000000000
--- a/changelogs/unreleased/41600-wider-project-readme-on-fixed-layout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make project README containers wider on fixed layout
-merge_request: 16181
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/41613-fix-redundant-modal.yml b/changelogs/unreleased/41613-fix-redundant-modal.yml
deleted file mode 100644
index 9e157b3065a..00000000000
--- a/changelogs/unreleased/41613-fix-redundant-modal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make modal dialog common for Groups tree app
-merge_request: 16311
-author:
-type: fixed
diff --git a/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml b/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml
deleted file mode 100644
index 48893862071..00000000000
--- a/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Only highlight search results under the highlighting size limit
-merge_request: 16462
-author:
-type: performance
diff --git a/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml b/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml
deleted file mode 100644
index 3a6fa425c9c..00000000000
--- a/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix file search results when they match file contents with a number between
- two colons
-merge_request: 16462
-author:
-type: fixed
diff --git a/changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml b/changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml
deleted file mode 100644
index 6b0d443e097..00000000000
--- a/changelogs/unreleased/41672-emphasize-gke-cluster-to-new-users.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add blue dot feature highlight to make GKE Clusters more visible to users
-merge_request: 16379
-author:
-type: added
diff --git a/changelogs/unreleased/41673-blank-query-members-api.yml b/changelogs/unreleased/41673-blank-query-members-api.yml
deleted file mode 100644
index 677c5e250c8..00000000000
--- a/changelogs/unreleased/41673-blank-query-members-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix error on empty query for Members API
-merge_request: 16235
-author:
-type: fixed
diff --git a/changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml b/changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml
deleted file mode 100644
index 51285e5476f..00000000000
--- a/changelogs/unreleased/41709-rich-blob-viewer-margins-for-pc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make rich blob viewer wider for PC
-merge_request: 16262
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/41731-predicate-memoization.yml b/changelogs/unreleased/41731-predicate-memoization.yml
deleted file mode 100644
index 110f78063f4..00000000000
--- a/changelogs/unreleased/41731-predicate-memoization.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Properly memoize some predicate methods
-merge_request: 16329
-author:
-type: performance
diff --git a/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml b/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml
deleted file mode 100644
index 03060c357fe..00000000000
--- a/changelogs/unreleased/41743-unused-selectors-for-cycle-analytics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unused CSS selectors for Cycle Analytics
-merge_request: 16270
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml b/changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml
deleted file mode 100644
index 593d3741a09..00000000000
--- a/changelogs/unreleased/41744-substitute-ui-charcoal-with-ui-indigo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Substitute deprecated ui_charcoal with new default ui_indigo
-merge_request: 16271
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml b/changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml
deleted file mode 100644
index 2a3d00f8e5f..00000000000
--- a/changelogs/unreleased/41749-postgres-9-6-for-ci-tests.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add reason to keep postgresql 9.2 for CI
-merge_request: 16277
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/41763-search-api.yml b/changelogs/unreleased/41763-search-api.yml
deleted file mode 100644
index 0a760a66510..00000000000
--- a/changelogs/unreleased/41763-search-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add search support into the API
-merge_request: 16878
-author:
-type: added
diff --git a/changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml b/changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml
deleted file mode 100644
index f64fd66ef79..00000000000
--- a/changelogs/unreleased/41771-reduce-cardinality-of-metrics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Reduce the number of Prometheus metrics
-merge_request: 16443
-author:
-type: performance
diff --git a/changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml b/changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml
deleted file mode 100644
index f23a6452b0d..00000000000
--- a/changelogs/unreleased/41802-add-space-to-edit-delete-tag-btns.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds spacing between edit and delete tag btn in tag list
-merge_request: 16757
-author: Jacopo Beschi @jacopo-beschi
-type: fixed
diff --git a/changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml b/changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml
deleted file mode 100644
index 146ae12afbd..00000000000
--- a/changelogs/unreleased/41807-15665-consistently-502s-because-it-fetches-every-commit.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Speed up loading merged merge requests when they contained a lot of commits
- before merging
-merge_request: 16320
-author:
-type: performance
diff --git a/changelogs/unreleased/41814-text-decoration-skip.yml b/changelogs/unreleased/41814-text-decoration-skip.yml
deleted file mode 100644
index 3e39d26be93..00000000000
--- a/changelogs/unreleased/41814-text-decoration-skip.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve readability of underlined links for dyslexic users
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml b/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml
new file mode 100644
index 00000000000..29ab7cc7cab
--- /dev/null
+++ b/changelogs/unreleased/41899-api-endpoint-for-importing-a-project-export.yml
@@ -0,0 +1,5 @@
+---
+title: API endpoint for importing a project export
+merge_request: 17025
+author:
+type: added
diff --git a/changelogs/unreleased/41949-move.yml b/changelogs/unreleased/41949-move.yml
new file mode 100644
index 00000000000..40ccac63a28
--- /dev/null
+++ b/changelogs/unreleased/41949-move.yml
@@ -0,0 +1,5 @@
+---
+title: Remember assignee when moving an issue
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml b/changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml
deleted file mode 100644
index 32a6f87d98e..00000000000
--- a/changelogs/unreleased/41956-fix-ctrl-enter-binding-to-save-comment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Ctrl+Enter keyboard shortcut saving comment/note edit
-merge_request: 16415
-author:
-type: fixed
diff --git a/changelogs/unreleased/42044-osw-add-button-to-deploy-runner-to-kubernetes.yml b/changelogs/unreleased/42044-osw-add-button-to-deploy-runner-to-kubernetes.yml
new file mode 100644
index 00000000000..6cf0de5b3fa
--- /dev/null
+++ b/changelogs/unreleased/42044-osw-add-button-to-deploy-runner-to-kubernetes.yml
@@ -0,0 +1,5 @@
+---
+title: Add a button to deploy a runner to a Kubernetes cluster in the settings page
+merge_request: 17278
+author:
+type: changed
diff --git a/changelogs/unreleased/42047-pg-10-support.yml b/changelogs/unreleased/42047-pg-10-support.yml
deleted file mode 100644
index f98e59329c3..00000000000
--- a/changelogs/unreleased/42047-pg-10-support.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support PostgreSQL 10
-merge_request: 16471
-author:
-type: added
diff --git a/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml b/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml
deleted file mode 100644
index 5cb5dc3ccd8..00000000000
--- a/changelogs/unreleased/42053-link-to-clusters-in-auto-devops-instead-of-kubernetes-service.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Link Auto DevOps settings to Clusters page
-merge_request: 16641
-author:
-type: changed
diff --git a/changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml b/changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml
deleted file mode 100644
index 2b043761856..00000000000
--- a/changelogs/unreleased/42055-update-marked-from-0-3-6-to-0-3-12.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update marked from 0.3.6 to 0.3.12
-merge_request: 16480
-author: Takuya Noguchi
-type: security
diff --git a/changelogs/unreleased/42154-fix-artifact-size-calc.yml b/changelogs/unreleased/42154-fix-artifact-size-calc.yml
deleted file mode 100644
index 3d6911abf09..00000000000
--- a/changelogs/unreleased/42154-fix-artifact-size-calc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix a bug calculating artifact size for project statistics
-merge_request: 16539
-author:
-type: fixed
diff --git a/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml b/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml
deleted file mode 100644
index ac8e4b034b5..00000000000
--- a/changelogs/unreleased/42157-41989-fix-duplicate-in-create-item-dropdown.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix duplicate item in protected branch/tag dropdown
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml b/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml
deleted file mode 100644
index 64340ab08cd..00000000000
--- a/changelogs/unreleased/42160-error-500-loading-merge-request-undefined-method-index-for-nil-nilclass.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 500 error when loading a merge request with an invalid comment
-merge_request: 16795
-author:
-type: fixed
diff --git a/changelogs/unreleased/42206-permit-password-for-git-param.yml b/changelogs/unreleased/42206-permit-password-for-git-param.yml
deleted file mode 100644
index 563dd528ad5..00000000000
--- a/changelogs/unreleased/42206-permit-password-for-git-param.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Permits 'password_authentication_enabled_for_git' parameter for ApplicationSettingsController
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/42220-add-pending-empty-state.yml b/changelogs/unreleased/42220-add-pending-empty-state.yml
deleted file mode 100644
index ad39578f2d9..00000000000
--- a/changelogs/unreleased/42220-add-pending-empty-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds empty state illustration for pending job
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml b/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml
deleted file mode 100644
index fbc589ea53d..00000000000
--- a/changelogs/unreleased/42231-protected-branches-api-route-returns-404-for-branches-with-dots.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix protected branches API to accept name parameter with dot
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/42251-explicit-timezone-for-karma.yml b/changelogs/unreleased/42251-explicit-timezone-for-karma.yml
deleted file mode 100644
index 25e0e774c48..00000000000
--- a/changelogs/unreleased/42251-explicit-timezone-for-karma.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Set timezone for karma to UTC
-merge_request: 16602
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml b/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml
deleted file mode 100644
index bd7e0d3a1b0..00000000000
--- a/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable MR check out button when source branch is deleted
-merge_request: 16631
-author: Jacopo Beschi @jacopo-beschi
-type: fixed
diff --git a/changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml b/changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml
deleted file mode 100644
index d7a8b6e6f81..00000000000
--- a/changelogs/unreleased/42270-fix-namespace-remove-exports-for-hashed-storage.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix export removal for hashed-storage projects within a renamed or deleted
- namespace
-merge_request: 16658
-author:
-type: fixed
diff --git a/changelogs/unreleased/42274-group-request-membership-long-too.yml b/changelogs/unreleased/42274-group-request-membership-long-too.yml
new file mode 100644
index 00000000000..03efedba638
--- /dev/null
+++ b/changelogs/unreleased/42274-group-request-membership-long-too.yml
@@ -0,0 +1,5 @@
+---
+title: Fix long list of recipients on group request membership email
+merge_request: 17121
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/42285-not-found-status-icon.yml b/changelogs/unreleased/42285-not-found-status-icon.yml
deleted file mode 100644
index ea7ff9d6ae7..00000000000
--- a/changelogs/unreleased/42285-not-found-status-icon.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace verified badge icons and uniform colors
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/42314-diff-file.yml b/changelogs/unreleased/42314-diff-file.yml
new file mode 100644
index 00000000000..1eed5ef1a34
--- /dev/null
+++ b/changelogs/unreleased/42314-diff-file.yml
@@ -0,0 +1,5 @@
+---
+title: Render modified icon for moved file in changes dropdown
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42332-actionview-template-error-366-524-out-of-range.yml b/changelogs/unreleased/42332-actionview-template-error-366-524-out-of-range.yml
new file mode 100644
index 00000000000..626c761bfbd
--- /dev/null
+++ b/changelogs/unreleased/42332-actionview-template-error-366-524-out-of-range.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 500 error being shown when diff has context marker with invalid encoding
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml b/changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml
new file mode 100644
index 00000000000..5613b2af763
--- /dev/null
+++ b/changelogs/unreleased/42431-add-auto-devops-and-clusters-button-to-projects.yml
@@ -0,0 +1,6 @@
+---
+title: Add a button on the project page to set up a Kubernetes cluster and enable
+ Auto DevOps
+merge_request: 16900
+author:
+type: added
diff --git a/changelogs/unreleased/42462-edit-note.yml b/changelogs/unreleased/42462-edit-note.yml
deleted file mode 100644
index 8df98f3ecef..00000000000
--- a/changelogs/unreleased/42462-edit-note.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix cnacel edit note button reverting changes
-merge_request: 42462
-author:
-type: fixed
diff --git a/changelogs/unreleased/42481-remove-notification-settings-left-projects.yml b/changelogs/unreleased/42481-remove-notification-settings-left-projects.yml
new file mode 100644
index 00000000000..ea99649131b
--- /dev/null
+++ b/changelogs/unreleased/42481-remove-notification-settings-left-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Remove user notification settings for groups and projects when user leaves
+merge_request: 16906
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/42497-rubocop-style-regexpliteral.yml b/changelogs/unreleased/42497-rubocop-style-regexpliteral.yml
deleted file mode 100644
index 6053883bac4..00000000000
--- a/changelogs/unreleased/42497-rubocop-style-regexpliteral.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable RuboCop Style/RegexpLiteral
-merge_request: 16752
-author: Takuya Noguchi
-type: other
diff --git a/changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml b/changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml
new file mode 100644
index 00000000000..a3dc1917001
--- /dev/null
+++ b/changelogs/unreleased/42509-fix-API-PUT-projects-fails-when-only-ci_config_path-is-specified.yml
@@ -0,0 +1,5 @@
+---
+title: Allow to call PUT /projects/:id API with only ci_config_path specified
+merge_request: 17105
+author: Laszlo Karpati
+type: fixed
diff --git a/changelogs/unreleased/42545-milestion-quick-actions-for-groups.yml b/changelogs/unreleased/42545-milestion-quick-actions-for-groups.yml
new file mode 100644
index 00000000000..d29f79aaaf8
--- /dev/null
+++ b/changelogs/unreleased/42545-milestion-quick-actions-for-groups.yml
@@ -0,0 +1,5 @@
+---
+title: Allows the usage of /milestone quick action for group milestones
+merge_request: 17239
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/42547-upload-store-mount-point.yml b/changelogs/unreleased/42547-upload-store-mount-point.yml
deleted file mode 100644
index 35ae022984e..00000000000
--- a/changelogs/unreleased/42547-upload-store-mount-point.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added uploader metadata to the uploads.
-merge_request: 16779
-author:
-type: added
diff --git a/changelogs/unreleased/42584-fix-margins-in-tag-list.yml b/changelogs/unreleased/42584-fix-margins-in-tag-list.yml
deleted file mode 100644
index 38b3dd85fd8..00000000000
--- a/changelogs/unreleased/42584-fix-margins-in-tag-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes different margins between buttons in tag list
-merge_request: 16927
-author: Jacopo Beschi @jacopo-beschi
-type: fixed
diff --git a/changelogs/unreleased/42591-update-nokogiri.yml b/changelogs/unreleased/42591-update-nokogiri.yml
deleted file mode 100644
index 5f9587d2d92..00000000000
--- a/changelogs/unreleased/42591-update-nokogiri.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update nokogiri to 1.8.2
-merge_request: 16807
-author:
-type: security
diff --git a/changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml b/changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml
deleted file mode 100644
index 24fcc38ee0e..00000000000
--- a/changelogs/unreleased/42669-allow-order_by-users-in-gitlab-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add sorting options for /users API (admin only)
-merge_request: 16945
-author:
-type: added
diff --git a/changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml b/changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml
deleted file mode 100644
index 0ef28e2ee01..00000000000
--- a/changelogs/unreleased/42684-set-up-ci-set-up-ci-cd.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rename button to enable CI/CD configuration to "Set up CI/CD"
-merge_request: 16870
-author:
-type: changed
diff --git a/changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml b/changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml
deleted file mode 100644
index aeadf8ffc4a..00000000000
--- a/changelogs/unreleased/42693-42693-add-a-link-to-documentation-on-how-to-get-external-ip-in-the-kubernetes-cluster-details-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a link to documentation on how to get external ip in the Kubernetes cluster details page
-merge_request: 16937
-author:
-type: added
diff --git a/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml b/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml
deleted file mode 100644
index c46cc86c99b..00000000000
--- a/changelogs/unreleased/42696-gitlab-import-leaves-group_id-on-projectlabel.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix GitLab import leaving group_id on ProjectLabel
-merge_request: 16877
-author:
-type: fixed
diff --git a/changelogs/unreleased/42730-close-rugged-repository.yml b/changelogs/unreleased/42730-close-rugged-repository.yml
deleted file mode 100644
index a632f5030a5..00000000000
--- a/changelogs/unreleased/42730-close-rugged-repository.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Close low level rugged repository in project cache worker
-merge_request: 16930
-author: Bastian Blank
-type: fixed
diff --git a/changelogs/unreleased/42800-change-usage-of-avatar_icon.yml b/changelogs/unreleased/42800-change-usage-of-avatar_icon.yml
new file mode 100644
index 00000000000..00f4b7436a7
--- /dev/null
+++ b/changelogs/unreleased/42800-change-usage-of-avatar_icon.yml
@@ -0,0 +1,6 @@
+---
+title: Use a user object in ApplicationHelper#avatar_icon where possible to avoid
+ N+1 queries.
+merge_request: 42800
+author:
+type: performance
diff --git a/changelogs/unreleased/42922-environment-name.yml b/changelogs/unreleased/42922-environment-name.yml
new file mode 100644
index 00000000000..0e9544245f6
--- /dev/null
+++ b/changelogs/unreleased/42922-environment-name.yml
@@ -0,0 +1,5 @@
+---
+title: Adds tooltip in environment names to increase readability
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42923-close-issue.yml b/changelogs/unreleased/42923-close-issue.yml
new file mode 100644
index 00000000000..e332bbf5dec
--- /dev/null
+++ b/changelogs/unreleased/42923-close-issue.yml
@@ -0,0 +1,5 @@
+---
+title: Fix close button on issues not working on mobile
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/42929-hide-new-variable-values.yml b/changelogs/unreleased/42929-hide-new-variable-values.yml
new file mode 100644
index 00000000000..68decd25b5a
--- /dev/null
+++ b/changelogs/unreleased/42929-hide-new-variable-values.yml
@@ -0,0 +1,5 @@
+---
+title: Hide CI secret variable values after saving
+merge_request: 17044
+author:
+type: changed
diff --git a/changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml b/changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml
new file mode 100644
index 00000000000..c1e9614b676
--- /dev/null
+++ b/changelogs/unreleased/43134-reduce-queries-pipelines-controller-show.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance of pipeline page by reducing DB queries
+merge_request: 17168
+author:
+type: performance
diff --git a/changelogs/unreleased/43198-fix-settings-panel-expanding-when-fragment-hash-linked.yml b/changelogs/unreleased/43198-fix-settings-panel-expanding-when-fragment-hash-linked.yml
new file mode 100644
index 00000000000..49ba48a0fef
--- /dev/null
+++ b/changelogs/unreleased/43198-fix-settings-panel-expanding-when-fragment-hash-linked.yml
@@ -0,0 +1,5 @@
+---
+title: Fix settings panels not expanding when fragment hash linked
+merge_request: 17074
+author:
+type: fixed
diff --git a/changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml b/changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml
new file mode 100644
index 00000000000..b527000332e
--- /dev/null
+++ b/changelogs/unreleased/43201-rename-repository-submit-button-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Allows project rename after validation error
+merge_request: 17150
+author:
+type: fixed
diff --git a/changelogs/unreleased/43261-fix-import-from-url-name-collision-active-tab.yml b/changelogs/unreleased/43261-fix-import-from-url-name-collision-active-tab.yml
new file mode 100644
index 00000000000..71073b2e214
--- /dev/null
+++ b/changelogs/unreleased/43261-fix-import-from-url-name-collision-active-tab.yml
@@ -0,0 +1,6 @@
+---
+title: Keep "Import project" tab/form active when validation fails trying to import
+ "Repo by URL"
+merge_request: 17136
+author:
+type: fixed
diff --git a/changelogs/unreleased/43261-fix-prometheus-installation.yml b/changelogs/unreleased/43261-fix-prometheus-installation.yml
new file mode 100644
index 00000000000..b5fc7980390
--- /dev/null
+++ b/changelogs/unreleased/43261-fix-prometheus-installation.yml
@@ -0,0 +1,5 @@
+---
+title: Allow Prometheus application to be installed from Cluster applications
+merge_request: 17372
+author:
+type: fixed
diff --git a/changelogs/unreleased/43275-improve-variables-validation-message.yml b/changelogs/unreleased/43275-improve-variables-validation-message.yml
new file mode 100644
index 00000000000..88ef93123a0
--- /dev/null
+++ b/changelogs/unreleased/43275-improve-variables-validation-message.yml
@@ -0,0 +1,5 @@
+---
+title: Remove duplicated error message on duplicate variable validation
+merge_request: 17135
+author:
+type: fixed
diff --git a/changelogs/unreleased/43315-gpg-popover.yml b/changelogs/unreleased/43315-gpg-popover.yml
new file mode 100644
index 00000000000..69238aa8075
--- /dev/null
+++ b/changelogs/unreleased/43315-gpg-popover.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes gpg popover layout
+merge_request: 17323
+author:
+type: fixed
diff --git a/changelogs/unreleased/43496-error-message-for-gke-clusters-persists-in-the-next-page.yml b/changelogs/unreleased/43496-error-message-for-gke-clusters-persists-in-the-next-page.yml
new file mode 100644
index 00000000000..c10b0e7a3cf
--- /dev/null
+++ b/changelogs/unreleased/43496-error-message-for-gke-clusters-persists-in-the-next-page.yml
@@ -0,0 +1,5 @@
+---
+title: Do not persist Google Project verification flash errors after a page reload
+merge_request: 17299
+author:
+type: fixed
diff --git a/changelogs/unreleased/43510-merge-requests-and-issues-don-t-show-for-all-subgroups.yml b/changelogs/unreleased/43510-merge-requests-and-issues-don-t-show-for-all-subgroups.yml
new file mode 100644
index 00000000000..e163c04f430
--- /dev/null
+++ b/changelogs/unreleased/43510-merge-requests-and-issues-don-t-show-for-all-subgroups.yml
@@ -0,0 +1,6 @@
+---
+title: Ensure group issues and merge requests pages show results from subgroups when
+ there are no results from the current group
+merge_request: 17312
+author:
+type: fixed
diff --git a/changelogs/unreleased/43532-error-on-admin-applications-prometheus-template.yml b/changelogs/unreleased/43532-error-on-admin-applications-prometheus-template.yml
new file mode 100644
index 00000000000..25bcbf2fbab
--- /dev/null
+++ b/changelogs/unreleased/43532-error-on-admin-applications-prometheus-template.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes Prometheus admin configuration page
+merge_request: 17377
+author:
+type: fixed
diff --git a/changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml b/changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml
new file mode 100644
index 00000000000..bda4ec84e5c
--- /dev/null
+++ b/changelogs/unreleased/43598-fix-duplicate-label-load-failure.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Group labels load failure when there are duplicate labels present
+merge_request: 17353
+author:
+type: fixed
diff --git a/changelogs/unreleased/4826-geo-wikisyncservice-attempts-to-sync-projects.yml b/changelogs/unreleased/4826-geo-wikisyncservice-attempts-to-sync-projects.yml
new file mode 100644
index 00000000000..7f1ccbfcc7e
--- /dev/null
+++ b/changelogs/unreleased/4826-geo-wikisyncservice-attempts-to-sync-projects.yml
@@ -0,0 +1,5 @@
+---
+title: Create empty wiki when import from GitLab and wiki is not there
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/4826-github-import-wiki-fix-1.yml b/changelogs/unreleased/4826-github-import-wiki-fix-1.yml
new file mode 100644
index 00000000000..69145cb6daf
--- /dev/null
+++ b/changelogs/unreleased/4826-github-import-wiki-fix-1.yml
@@ -0,0 +1,5 @@
+---
+title: "[GitHub Import] Create an empty wiki if wiki import failed"
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-confirmation-input-for-modals.yml b/changelogs/unreleased/add-confirmation-input-for-modals.yml
deleted file mode 100644
index ff1027bc55a..00000000000
--- a/changelogs/unreleased/add-confirmation-input-for-modals.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add confirmation-input component
-merge_request: 16816
-author:
-type: other
diff --git a/changelogs/unreleased/api-refs-for-commit.yml b/changelogs/unreleased/api-refs-for-commit.yml
new file mode 100644
index 00000000000..df8a2b0eccc
--- /dev/null
+++ b/changelogs/unreleased/api-refs-for-commit.yml
@@ -0,0 +1,5 @@
+---
+title: 'API: Get references a commit is pushed to'
+merge_request: 15026
+author: Robert Schilling
+type: added
diff --git a/changelogs/unreleased/asciidoc_inter_document_cross_references.yml b/changelogs/unreleased/asciidoc_inter_document_cross_references.yml
new file mode 100644
index 00000000000..34b26753312
--- /dev/null
+++ b/changelogs/unreleased/asciidoc_inter_document_cross_references.yml
@@ -0,0 +1,5 @@
+---
+title: Asciidoc now support inter-document cross references between files in repository
+merge_request: 17125
+author: Turo Soisenniemi
+type: changed
diff --git a/changelogs/unreleased/bump-workhorse.yml b/changelogs/unreleased/bump-workhorse.yml
deleted file mode 100644
index 37ee402dac7..00000000000
--- a/changelogs/unreleased/bump-workhorse.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade GitLab Workhorse to v3.6.0
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml b/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml
deleted file mode 100644
index 378f0ef7ce9..00000000000
--- a/changelogs/unreleased/bvl-fix-500-on-fork-without-restricted-visibility-levels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix forking projects when no restricted visibility levels are defined applicationwide
-merge_request: 16881
-author:
-type: fixed
diff --git a/changelogs/unreleased/change-strip-whitespace-from-username-input-42637.yml b/changelogs/unreleased/change-strip-whitespace-from-username-input-42637.yml
new file mode 100644
index 00000000000..a51781396ee
--- /dev/null
+++ b/changelogs/unreleased/change-strip-whitespace-from-username-input-42637.yml
@@ -0,0 +1,5 @@
+---
+title: Remove whitespace from the username/email sign in form field
+merge_request: 17020
+author: Peter lauck
+type: changed
diff --git a/changelogs/unreleased/contribution_calendar_label_cut_off.yml b/changelogs/unreleased/contribution_calendar_label_cut_off.yml
deleted file mode 100644
index 0b4a746bab8..00000000000
--- a/changelogs/unreleased/contribution_calendar_label_cut_off.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Contribution calendar label was cut off
-merge_request:
-author: Branka Martinovic
-type: fixed
diff --git a/changelogs/unreleased/cs-fix-commercial-content-check.yml b/changelogs/unreleased/cs-fix-commercial-content-check.yml
deleted file mode 100644
index fec80e3ecd2..00000000000
--- a/changelogs/unreleased/cs-fix-commercial-content-check.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix version information not showing on help page if commercial content display
- was disabled.
-merge_request: 16743
-author:
-type: fixed
diff --git a/changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml b/changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml
deleted file mode 100644
index 5b850c92d17..00000000000
--- a/changelogs/unreleased/da-verify-integrity-of-uploaded-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add rake task to check integrity of uploaded files
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/default-to-https-for-gravatar-urls.yml b/changelogs/unreleased/default-to-https-for-gravatar-urls.yml
deleted file mode 100644
index 544c34fe31d..00000000000
--- a/changelogs/unreleased/default-to-https-for-gravatar-urls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Default to HTTPS for all Gravatar URLs
-merge_request: 16666
-author:
-type: fixed
diff --git a/changelogs/unreleased/disable-throwOnError-in-katex.yml b/changelogs/unreleased/disable-throwOnError-in-katex.yml
deleted file mode 100644
index 0cd17bb29fe..00000000000
--- a/changelogs/unreleased/disable-throwOnError-in-katex.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable throwOnError in KaTeX to reveal user where is the problem
-merge_request: 16684
-author: Jakub Jirutka
-type: other
diff --git a/changelogs/unreleased/display-mr-in-commit-page.yml b/changelogs/unreleased/display-mr-in-commit-page.yml
deleted file mode 100644
index a9224c00b66..00000000000
--- a/changelogs/unreleased/display-mr-in-commit-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add link on commit page to merge request that introduced that commit
-merge_request: 13713
-author: Hiroyuki Sato
-type: added
diff --git a/changelogs/unreleased/dm-dont-cache-nil-root-ref.yml b/changelogs/unreleased/dm-dont-cache-nil-root-ref.yml
new file mode 100644
index 00000000000..4dab7d0ffca
--- /dev/null
+++ b/changelogs/unreleased/dm-dont-cache-nil-root-ref.yml
@@ -0,0 +1,5 @@
+---
+title: Don't cache a nil repository root ref to prevent caching issues
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/dm-escape-commit-message.yml b/changelogs/unreleased/dm-escape-commit-message.yml
new file mode 100644
index 00000000000..89af2da3484
--- /dev/null
+++ b/changelogs/unreleased/dm-escape-commit-message.yml
@@ -0,0 +1,5 @@
+---
+title: Escape HTML entities in commit messages
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/36571-ignore-root-in-repo.yml b/changelogs/unreleased/dm-go-get-api-token.yml
index 396e82be51b..ad9cfe05849 100644
--- a/changelogs/unreleased/36571-ignore-root-in-repo.yml
+++ b/changelogs/unreleased/dm-go-get-api-token.yml
@@ -1,5 +1,5 @@
---
-title: Ignore leading slashes when searching for files within context of repository.
+title: Allow token authentication on go-get request
merge_request:
-author: Andrew McCallum
-type: fixed
+author:
+type: changed
diff --git a/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml b/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml
deleted file mode 100644
index f59021c0ec9..00000000000
--- a/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Execute system hooks after-commit when executing project hooks
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dm-route-path-validation.yml b/changelogs/unreleased/dm-route-path-validation.yml
deleted file mode 100644
index df3ed1de1b9..00000000000
--- a/changelogs/unreleased/dm-route-path-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Validate user, group and project paths consistently, and only once
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dm-stuck-import-jobs-verify.yml b/changelogs/unreleased/dm-stuck-import-jobs-verify.yml
new file mode 100644
index 00000000000..ed2c2d30f0d
--- /dev/null
+++ b/changelogs/unreleased/dm-stuck-import-jobs-verify.yml
@@ -0,0 +1,5 @@
+---
+title: Verify project import status again before marking as failed
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/dm-user-namespace-route-path-validation.yml b/changelogs/unreleased/dm-user-namespace-route-path-validation.yml
deleted file mode 100644
index 36615e5b976..00000000000
--- a/changelogs/unreleased/dm-user-namespace-route-path-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Validate user namespace before saving so that errors persist on model
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/docs-update-vue-naming-guidelines.yml b/changelogs/unreleased/docs-update-vue-naming-guidelines.yml
new file mode 100644
index 00000000000..95bfd212370
--- /dev/null
+++ b/changelogs/unreleased/docs-update-vue-naming-guidelines.yml
@@ -0,0 +1,5 @@
+---
+title: Update vue component naming guidelines
+merge_request: 17018
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml b/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml
deleted file mode 100644
index 3fce53bc941..00000000000
--- a/changelogs/unreleased/feat-add-section-headers-to-plus-button-dropdown.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add section headers to plus button dropdown
-merge_request: 16394
-author: George Tsiolis
-type: added
diff --git a/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml b/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml
deleted file mode 100644
index 8f3459a7381..00000000000
--- a/changelogs/unreleased/feat-add-section-headers-to-project-repo-buttons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve empty project overview
-merge_request: 16617
-author: George Tsiolis
-type: added
diff --git a/changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml b/changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml
new file mode 100644
index 00000000000..ca877d32b05
--- /dev/null
+++ b/changelogs/unreleased/feature--2848-display-time-tracking-totals-milestone-page.yml
@@ -0,0 +1,5 @@
+---
+title: "#28481: Display time tracking totals on milestone page"
+merge_request: 16753
+author: Riccardo Padovani
+type: added
diff --git a/changelogs/unreleased/feature-26598-clear-button-ci-lint.yml b/changelogs/unreleased/feature-26598-clear-button-ci-lint.yml
new file mode 100644
index 00000000000..fcf237f20f0
--- /dev/null
+++ b/changelogs/unreleased/feature-26598-clear-button-ci-lint.yml
@@ -0,0 +1,4 @@
+---
+title: Added clear button to ci lint editor
+merge_request:
+author: Michael Robinson
diff --git a/changelogs/unreleased/feature-39591-visibility-level.yml b/changelogs/unreleased/feature-39591-visibility-level.yml
deleted file mode 100644
index 4bbc9bdbb2e..00000000000
--- a/changelogs/unreleased/feature-39591-visibility-level.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Open visibility level help in a new tab
-merge_request:
-author: Jussi Räsänen
-type: fixed
diff --git a/changelogs/unreleased/feature-include-custom-attributes-in-api.yml b/changelogs/unreleased/feature-include-custom-attributes-in-api.yml
new file mode 100644
index 00000000000..f1087d7f7cc
--- /dev/null
+++ b/changelogs/unreleased/feature-include-custom-attributes-in-api.yml
@@ -0,0 +1,5 @@
+---
+title: Allow including custom attributes in API responses
+merge_request: 16526
+author: Markus Koller
+type: changed
diff --git a/changelogs/unreleased/feature-merge-request-system-hook.yml b/changelogs/unreleased/feature-merge-request-system-hook.yml
deleted file mode 100644
index cfc4c4235d6..00000000000
--- a/changelogs/unreleased/feature-merge-request-system-hook.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: System hooks for Merge Requests
-merge_request: 14387
-author: Alexis Reigel
-type: added
diff --git a/changelogs/unreleased/feature-oidc-groups-claim.yml b/changelogs/unreleased/feature-oidc-groups-claim.yml
new file mode 100644
index 00000000000..bde19130114
--- /dev/null
+++ b/changelogs/unreleased/feature-oidc-groups-claim.yml
@@ -0,0 +1,4 @@
+---
+title: Add groups to OpenID Connect claims
+merge_request: 16929
+author: Hassan Zamani
diff --git a/changelogs/unreleased/feature-sm-artifacts-trace.yml b/changelogs/unreleased/feature-sm-artifacts-trace.yml
deleted file mode 100644
index 7654ce58aeb..00000000000
--- a/changelogs/unreleased/feature-sm-artifacts-trace.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Save traces as artifacts
-merge_request: 16702
-author:
-type: changed
diff --git a/changelogs/unreleased/file-content-large-screen-padding.yml b/changelogs/unreleased/file-content-large-screen-padding.yml
deleted file mode 100644
index 5691cd09b1f..00000000000
--- a/changelogs/unreleased/file-content-large-screen-padding.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Double padding for file-content wiki class on larger screens
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml b/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml
deleted file mode 100644
index d8e97b7ad04..00000000000
--- a/changelogs/unreleased/fix-add-horizontal-scroll-to-wiki-tables.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add horizontal scroll to wiki tables
-merge_request: 16527
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml b/changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml
deleted file mode 100644
index b79ec2944fd..00000000000
--- a/changelogs/unreleased/fix-adjust-button-group-width-on-mobile.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change button group width on mobile
-merge_request: 16726
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml b/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml
deleted file mode 100644
index 2e0f59f81e9..00000000000
--- a/changelogs/unreleased/fix-adjust-layout-width-for-fixed-layout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust layout width for fixed layout
-merge_request: 16337
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-change-event-body-label-font-size.yml b/changelogs/unreleased/fix-change-event-body-label-font-size.yml
new file mode 100644
index 00000000000..3192a7bff92
--- /dev/null
+++ b/changelogs/unreleased/fix-change-event-body-label-font-size.yml
@@ -0,0 +1,5 @@
+---
+title: Apply new default and inline label design
+merge_request: 16956
+author: George Tsiolis
+type: changed
diff --git a/changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml b/changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml
deleted file mode 100644
index 2f6a07bb234..00000000000
--- a/changelogs/unreleased/fix-dashboard-projects-nav-links-height.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix dashboard projects nav links height
-merge_request: 16204
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml b/changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml
deleted file mode 100644
index 31b4734bc79..00000000000
--- a/changelogs/unreleased/fix-gb-improve-manual-action-tooltips.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix tooltip displayed for running manual actions
-merge_request: 16489
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-improve-issue-note-dropdown.yml b/changelogs/unreleased/fix-improve-issue-note-dropdown.yml
deleted file mode 100644
index aaf4811c64a..00000000000
--- a/changelogs/unreleased/fix-improve-issue-note-dropdown.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve issue note dropdown and mr button
-merge_request: 16758
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/fix-install-docs.yml b/changelogs/unreleased/fix-install-docs.yml
deleted file mode 100644
index c2c0dd1364b..00000000000
--- a/changelogs/unreleased/fix-install-docs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update minimum git version to 2.9.5
-merge_request: 16683
-author:
-type: other
diff --git a/changelogs/unreleased/fix-new-project-path-input-overlapping.yml b/changelogs/unreleased/fix-new-project-path-input-overlapping.yml
new file mode 100644
index 00000000000..fb33ce9437a
--- /dev/null
+++ b/changelogs/unreleased/fix-new-project-path-input-overlapping.yml
@@ -0,0 +1,5 @@
+---
+title: Fix new project path input overlapping
+merge_request: 16755
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml b/changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml
deleted file mode 100644
index 883eecabe04..00000000000
--- a/changelogs/unreleased/fix-show-sidebar-sub-level-items-for-billing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Override group sidebar links
-merge_request: 16942
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/fix-squash-with-renamed-files.yml b/changelogs/unreleased/fix-squash-with-renamed-files.yml
new file mode 100644
index 00000000000..f7cd3a84367
--- /dev/null
+++ b/changelogs/unreleased/fix-squash-with-renamed-files.yml
@@ -0,0 +1,5 @@
+---
+title: Fix squashing when a file is renamed
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-template-project-visibility.yml b/changelogs/unreleased/fix-template-project-visibility.yml
new file mode 100644
index 00000000000..6576097822b
--- /dev/null
+++ b/changelogs/unreleased/fix-template-project-visibility.yml
@@ -0,0 +1,5 @@
+---
+title: Respect description and visibility when creating project from template
+merge_request: 16820
+author: George Tsiolis
+type: fixed
diff --git a/changelogs/unreleased/fix_gitlab-ce-41891.yml b/changelogs/unreleased/fix_gitlab-ce-41891.yml
deleted file mode 100644
index 56bdc1a7c32..00000000000
--- a/changelogs/unreleased/fix_gitlab-ce-41891.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Fix custom header logo design nitpick: Remove unneeded margin on empty logo text'
-merge_request: 16383
-author: Markus Doits
-type: fixed
diff --git a/changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml b/changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml
deleted file mode 100644
index 77142528be2..00000000000
--- a/changelogs/unreleased/fj-22607-lowercase-usernames-from-ldap.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added ldap config setting to lower case the username
-merge_request: 16791
-author:
-type: added
diff --git a/changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml b/changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml
deleted file mode 100644
index 5b5310dcfef..00000000000
--- a/changelogs/unreleased/fj-37273-moving-wiki-pages-from-the-ui.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow moving wiki pages from the UI
-merge_request: 16313
-author:
-type: fixed
diff --git a/changelogs/unreleased/fj-42910-unauthenticated-limit-via-ssh.yml b/changelogs/unreleased/fj-42910-unauthenticated-limit-via-ssh.yml
new file mode 100644
index 00000000000..cef339ef787
--- /dev/null
+++ b/changelogs/unreleased/fj-42910-unauthenticated-limit-via-ssh.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed bug with unauthenticated requests through git ssh
+merge_request: 17149
+author:
+type: fixed
diff --git a/changelogs/unreleased/fl-mr-widget-refactor.yml b/changelogs/unreleased/fl-mr-widget-refactor.yml
deleted file mode 100644
index d59cca68409..00000000000
--- a/changelogs/unreleased/fl-mr-widget-refactor.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refactors mr widget components into vue files and adds i18n
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/fl-refresh-btn.yml b/changelogs/unreleased/fl-refresh-btn.yml
new file mode 100644
index 00000000000..640fdda9ce7
--- /dev/null
+++ b/changelogs/unreleased/fl-refresh-btn.yml
@@ -0,0 +1,5 @@
+---
+title: Show loading button inline in refresh button in MR widget
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/gitaly-git-http-ssh.yml b/changelogs/unreleased/gitaly-git-http-ssh.yml
deleted file mode 100644
index 98812e92e2a..00000000000
--- a/changelogs/unreleased/gitaly-git-http-ssh.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Default to Gitaly for 'git push' HTTP/SSH, and make Gitaly mandatory for SSH
- pull
-merge_request: 16586
-author:
-type: other
diff --git a/changelogs/unreleased/gitaly-repo-exists.yml b/changelogs/unreleased/gitaly-repo-exists.yml
deleted file mode 100644
index a9eb42a2038..00000000000
--- a/changelogs/unreleased/gitaly-repo-exists.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make Gitaly RepositoryExists opt-out
-merge_request: 16680
-author:
-type: other
diff --git a/changelogs/unreleased/group-label-page-breadcrumb.yml b/changelogs/unreleased/group-label-page-breadcrumb.yml
new file mode 100644
index 00000000000..c6cc4618c52
--- /dev/null
+++ b/changelogs/unreleased/group-label-page-breadcrumb.yml
@@ -0,0 +1,5 @@
+---
+title: Fix breadcrumb on labels page for groups
+merge_request: 17045
+author: Onuwa Nnachi Isaac
+type: fixed
diff --git a/changelogs/unreleased/grpc-unavailable-restart.yml b/changelogs/unreleased/grpc-unavailable-restart.yml
new file mode 100644
index 00000000000..5ce08d66004
--- /dev/null
+++ b/changelogs/unreleased/grpc-unavailable-restart.yml
@@ -0,0 +1,5 @@
+---
+title: Restart Unicorn and Sidekiq when GRPC throws 14:Endpoint read failed
+merge_request: 17293
+author:
+type: fixed
diff --git a/changelogs/unreleased/internationalize-charts-page.yml b/changelogs/unreleased/internationalize-charts-page.yml
deleted file mode 100644
index 481b83fb059..00000000000
--- a/changelogs/unreleased/internationalize-charts-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Internationalize charts page
-merge_request: 16687
-author: selrahman
-type: changed
diff --git a/changelogs/unreleased/internationalize-graph-page.yml b/changelogs/unreleased/internationalize-graph-page.yml
deleted file mode 100644
index 904dbd606d7..00000000000
--- a/changelogs/unreleased/internationalize-graph-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Internationalize graph page selrahman
-merge_request: 16688
-author: Shah El-Rahman
-type: changed
diff --git a/changelogs/unreleased/issue-39885.yml b/changelogs/unreleased/issue-39885.yml
new file mode 100644
index 00000000000..75bf9434152
--- /dev/null
+++ b/changelogs/unreleased/issue-39885.yml
@@ -0,0 +1,5 @@
+---
+title: 'Ensure users cannot create environments with leading or trailing slashes (Fixes #39885)'
+merge_request: 15273
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue-42689-new-file-template.yml b/changelogs/unreleased/issue-42689-new-file-template.yml
deleted file mode 100644
index d6b77b87605..00000000000
--- a/changelogs/unreleased/issue-42689-new-file-template.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Trigger change event on filename input when file template is applied
-merge_request: 16911
-author: Sebastian Klingler
-type: fixed
diff --git a/changelogs/unreleased/issue-edit-shortcut.yml b/changelogs/unreleased/issue-edit-shortcut.yml
new file mode 100644
index 00000000000..2b29b2bc03f
--- /dev/null
+++ b/changelogs/unreleased/issue-edit-shortcut.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed issue edit shortcut not opening edit form
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/issue_41460.yml b/changelogs/unreleased/issue_41460.yml
deleted file mode 100644
index 24d3eae6bf8..00000000000
--- a/changelogs/unreleased/issue_41460.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix error on changes tab when merge request cannot be created
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/issues-closed-at-steal.yml b/changelogs/unreleased/issues-closed-at-steal.yml
deleted file mode 100644
index a5f0898995f..00000000000
--- a/changelogs/unreleased/issues-closed-at-steal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Finish any remaining jobs for issues.closed_at
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/jej-upload-file-tracks-lfs.yml b/changelogs/unreleased/jej-upload-file-tracks-lfs.yml
deleted file mode 100644
index a7cf6b6ba2c..00000000000
--- a/changelogs/unreleased/jej-upload-file-tracks-lfs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: File Upload UI can create LFS pointers based on .gitattributes
-merge_request: 16412
-author:
-type: fixed
diff --git a/changelogs/unreleased/merge-request-target-branch-perf.yml b/changelogs/unreleased/merge-request-target-branch-perf.yml
deleted file mode 100644
index 37e326bfde3..00000000000
--- a/changelogs/unreleased/merge-request-target-branch-perf.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance of target branch dropdown
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/minimal-fix-for-artifacts-service.yml b/changelogs/unreleased/minimal-fix-for-artifacts-service.yml
new file mode 100644
index 00000000000..11f5bc17759
--- /dev/null
+++ b/changelogs/unreleased/minimal-fix-for-artifacts-service.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent trace artifact migration to incur data loss
+merge_request: 17313
+author:
+type: fixed
diff --git a/changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml b/changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml
new file mode 100644
index 00000000000..a761d610da1
--- /dev/null
+++ b/changelogs/unreleased/mk-fix-error-code-for-repo-does-not-exist.yml
@@ -0,0 +1,5 @@
+---
+title: Return a 404 instead of 403 if the repository does not exist on disk
+merge_request: 17341
+author:
+type: fixed
diff --git a/changelogs/unreleased/move-board-list-vue-component.yml b/changelogs/unreleased/move-board-list-vue-component.yml
deleted file mode 100644
index 9c566b43cc2..00000000000
--- a/changelogs/unreleased/move-board-list-vue-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move BoardList vue component to vue file
-merge_request: 16888
-author: George Tsiolis
-type: performance
diff --git a/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml b/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml
deleted file mode 100644
index 1cffb213f23..00000000000
--- a/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Close and do not reload MR diffs when source branch is deleted
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml b/changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml
deleted file mode 100644
index b2c1cd9710a..00000000000
--- a/changelogs/unreleased/osw-markdown-bypass-for-commit-messages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bypass commits title markdown on notes
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml b/changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml
deleted file mode 100644
index 03940555162..00000000000
--- a/changelogs/unreleased/osw-remove-duplicate-can-be-reverted-calls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove duplicate calls of MergeRequest#can_be_reverted?
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml b/changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml
deleted file mode 100644
index 62931218861..00000000000
--- a/changelogs/unreleased/osw-short-circuit-mergeable-disccusions-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Stop checking if discussions are in a mergeable state if the MR isn't
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/osw-system-notes-for-commits-regression.yml b/changelogs/unreleased/osw-system-notes-for-commits-regression.yml
deleted file mode 100644
index 6d0943c7716..00000000000
--- a/changelogs/unreleased/osw-system-notes-for-commits-regression.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Reload MRs memoization after diffs creation
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml b/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml
deleted file mode 100644
index 3854985e576..00000000000
--- a/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Return more consistent values for merge_status on MR APIs
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml b/changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml
deleted file mode 100644
index b2bb173912a..00000000000
--- a/changelogs/unreleased/pawel-connect_to_prometheus_through_proxy-30480.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Implement multi server support and use kube proxy to connect to Prometheus
- servers inside K8S cluster
-merge_request: 16182
-author:
-type: added
diff --git a/changelogs/unreleased/persistent-callouts.yml b/changelogs/unreleased/persistent-callouts.yml
deleted file mode 100644
index ca949a3b96c..00000000000
--- a/changelogs/unreleased/persistent-callouts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add backend for persistently dismissably callouts
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/query-counts.yml b/changelogs/unreleased/query-counts.yml
deleted file mode 100644
index e01ff8a4ad8..00000000000
--- a/changelogs/unreleased/query-counts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Track and act upon the number of executed queries
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml b/changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml
deleted file mode 100644
index d43675e175d..00000000000
--- a/changelogs/unreleased/refactor-ci-variable-list-for-future-usage-in-4110.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Hide variable values on pipeline schedule edit page
-merge_request: 16729
-author:
-type: changed
diff --git a/changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml b/changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml
new file mode 100644
index 00000000000..20d05530513
--- /dev/null
+++ b/changelogs/unreleased/refactor-move-board-new-issue-vue-component.yml
@@ -0,0 +1,5 @@
+---
+title: Move BoardNewIssue vue component
+merge_request: 16947
+author: George Tsiolis
+type: performance
diff --git a/changelogs/unreleased/refactor-move-filtered-search-vue-component.yml b/changelogs/unreleased/refactor-move-filtered-search-vue-component.yml
new file mode 100644
index 00000000000..d65318d7ba1
--- /dev/null
+++ b/changelogs/unreleased/refactor-move-filtered-search-vue-component.yml
@@ -0,0 +1,5 @@
+---
+title: Move RecentSearchesDropdownContent vue component
+merge_request: 16951
+author: George Tsiolis
+type: performance
diff --git a/changelogs/unreleased/refactor-move-issuable-time-tracker-vue-component.yml b/changelogs/unreleased/refactor-move-issuable-time-tracker-vue-component.yml
new file mode 100644
index 00000000000..5ed06c61817
--- /dev/null
+++ b/changelogs/unreleased/refactor-move-issuable-time-tracker-vue-component.yml
@@ -0,0 +1,5 @@
+---
+title: Move IssuableTimeTracker vue component
+merge_request: 16948
+author: George Tsiolis
+type: performance
diff --git a/changelogs/unreleased/remove-unnecessary-validate-project.yml b/changelogs/unreleased/remove-unnecessary-validate-project.yml
new file mode 100644
index 00000000000..ebc8da03dd8
--- /dev/null
+++ b/changelogs/unreleased/remove-unnecessary-validate-project.yml
@@ -0,0 +1,5 @@
+---
+title: 'Remove unecessary validate: true from belongs_to :project'
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-add-gitaly-health-check.yml b/changelogs/unreleased/sh-add-gitaly-health-check.yml
deleted file mode 100644
index 32c4c5362b4..00000000000
--- a/changelogs/unreleased/sh-add-gitaly-health-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add a gRPC health check to ensure Gitaly is up
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/sh-fix-award-emoji-move-issues.yml b/changelogs/unreleased/sh-fix-award-emoji-move-issues.yml
deleted file mode 100644
index c62fad927d0..00000000000
--- a/changelogs/unreleased/sh-fix-award-emoji-move-issues.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix bug where award emojis would be lost when moving issues between projects
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-events-collection.yml b/changelogs/unreleased/sh-fix-events-collection.yml
deleted file mode 100644
index 50af39d9caf..00000000000
--- a/changelogs/unreleased/sh-fix-events-collection.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix not all events being shown in group dashboard
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml b/changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml
new file mode 100644
index 00000000000..5b4bbe0dc7a
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-geo-error-500-gpg-commit.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Error 500 when viewing a commit with a GPG signature in Geo
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-jira-trailing-slash.yml b/changelogs/unreleased/sh-fix-jira-trailing-slash.yml
deleted file mode 100644
index 786f6cd3727..00000000000
--- a/changelogs/unreleased/sh-fix-jira-trailing-slash.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix JIRA not working when a trailing slash is included
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-project-members-api-perf.yml b/changelogs/unreleased/sh-fix-project-members-api-perf.yml
deleted file mode 100644
index c3fff933547..00000000000
--- a/changelogs/unreleased/sh-fix-project-members-api-perf.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Remove N+1 queries with /projects/:project_id/{access_requests,members} API
- endpoints
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-log-when-user-blocked.yml b/changelogs/unreleased/sh-log-when-user-blocked.yml
deleted file mode 100644
index 9abf2017514..00000000000
--- a/changelogs/unreleased/sh-log-when-user-blocked.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Log and send a system hook if a blocked user attempts to login
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/sh-remove-shared-runners-and-more.yml b/changelogs/unreleased/sh-remove-shared-runners-and-more.yml
deleted file mode 100644
index cc079617883..00000000000
--- a/changelogs/unreleased/sh-remove-shared-runners-and-more.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove erroneous text in shared runners page that suggested more runners available
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-store-user-in-api-logs.yml b/changelogs/unreleased/sh-store-user-in-api-logs.yml
deleted file mode 100644
index d904dcaf6d3..00000000000
--- a/changelogs/unreleased/sh-store-user-in-api-logs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Save user ID and username in Grape API log (api_json.log)
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/style-include-branch-in-mobile-view.yml b/changelogs/unreleased/style-include-branch-in-mobile-view.yml
deleted file mode 100644
index 5c8ef86992d..00000000000
--- a/changelogs/unreleased/style-include-branch-in-mobile-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include branch in mobile view for pipelines
-merge_request: 16910
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/update-node-docs.yml b/changelogs/unreleased/update-node-docs.yml
deleted file mode 100644
index a1d9d12f0ca..00000000000
--- a/changelogs/unreleased/update-node-docs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix documentation about node version
-merge_request: 16720
-author: Tobias Gurtzick
-type: other
diff --git a/changelogs/unreleased/ux-guide-deprecation.yml b/changelogs/unreleased/ux-guide-deprecation.yml
deleted file mode 100644
index 16477f59abf..00000000000
--- a/changelogs/unreleased/ux-guide-deprecation.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Add note within ux documentation that further changes should be made within
- the design.gitlab project
-merge_request:
-author:
-type: deprecated
diff --git a/changelogs/unreleased/winh-delete-milestone-modal.yml b/changelogs/unreleased/winh-delete-milestone-modal.yml
deleted file mode 100644
index 6517fbd5f63..00000000000
--- a/changelogs/unreleased/winh-delete-milestone-modal.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add modal for deleting a milestone
-merge_request: 16229
-author:
-type: other
diff --git a/changelogs/unreleased/winh-kubernetes-clusters.yml b/changelogs/unreleased/winh-kubernetes-clusters.yml
deleted file mode 100644
index 387a719848d..00000000000
--- a/changelogs/unreleased/winh-kubernetes-clusters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Replace "cluster" with "Kubernetes cluster"
-merge_request: 16778
-author:
-type: changed
diff --git a/changelogs/unreleased/winh-new-modal-component.yml b/changelogs/unreleased/winh-new-modal-component.yml
new file mode 100644
index 00000000000..bcc0d489c88
--- /dev/null
+++ b/changelogs/unreleased/winh-new-modal-component.yml
@@ -0,0 +1,5 @@
+---
+title: Add new modal Vue component
+merge_request: 17108
+author:
+type: changed
diff --git a/changelogs/unreleased/winh-search-page-filters.yml b/changelogs/unreleased/winh-search-page-filters.yml
deleted file mode 100644
index 90c5cd8d818..00000000000
--- a/changelogs/unreleased/winh-search-page-filters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Filter groups and projects dropdowns of search page on backend
-merge_request: 16336
-author:
-type: fixed
diff --git a/changelogs/unreleased/winh-style-modals.yml b/changelogs/unreleased/winh-style-modals.yml
deleted file mode 100644
index b7d0293960d..00000000000
--- a/changelogs/unreleased/winh-style-modals.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust modal style to new design
-merge_request: 16310
-author:
-type: other
diff --git a/changelogs/unreleased/zj-gitaly-server-info.yml b/changelogs/unreleased/zj-gitaly-server-info.yml
deleted file mode 100644
index cf6295f2bbc..00000000000
--- a/changelogs/unreleased/zj-gitaly-server-info.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Gitaly Servers admin dashboard
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/zj-protobuf.yml b/changelogs/unreleased/zj-protobuf.yml
deleted file mode 100644
index 830c2e82da9..00000000000
--- a/changelogs/unreleased/zj-protobuf.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Downgrade google-protobuf gem
-merge_request: 16941
-author:
-type: other
diff --git a/config/application.rb b/config/application.rb
index 751307de975..918bd4d57cf 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -11,6 +11,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/redis/queues')
require_dependency Rails.root.join('lib/gitlab/redis/shared_state')
require_dependency Rails.root.join('lib/gitlab/request_context')
+ require_dependency Rails.root.join('lib/gitlab/current_settings')
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
@@ -68,6 +69,7 @@ module Gitlab
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
# - Deploy keys (:key)
+ # - Secret variable values (:value)
config.filter_parameters += [/token$/, /password/, /secret/]
config.filter_parameters += %i(
certificate
@@ -79,6 +81,7 @@ module Gitlab
sentry_dsn
trace
variables
+ value
)
# Enable escaping HTML in JSON.
@@ -107,8 +110,6 @@ module Gitlab
config.assets.precompile << "print.css"
config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css"
- config.assets.precompile << "katex.css"
- config.assets.precompile << "katex.js"
config.assets.precompile << "xterm/xterm.css"
config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js"
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 778cca4297f..6616b85129e 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -467,8 +467,8 @@
- - :license
- pikaday
- MIT
- - :who:
- :why:
+ - :who:
+ :why:
:versions: []
:when: 2017-10-17 17:46:12.367554000 Z
- - :license
@@ -516,3 +516,21 @@
:why: zlib license + Development Lib + https://github.com/uNetworking/uWebSockets/blob/master/LICENSE
:versions: []
:when: 2018-01-17 23:46:12.367554000 Z
+- - :approve
+ - atob
+ - :who: Mike Greiling
+ :why: https://github.com/node-browser-compat/atob/blob/master/LICENSE
+ :versions: []
+ :when: 2018-02-20 19:42:08.409887000 Z
+- - :approve
+ - cyclist
+ - :who: Mike Greiling
+ :why: https://github.com/mafintosh/cyclist/blob/master/LICENSE
+ :versions: []
+ :when: 2018-02-20 21:37:43.774978000 Z
+- - :approve
+ - bitsyntax
+ - :who: Mike Greiling
+ :why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT
+ :versions: []
+ :when: 2018-02-20 22:20:25.958123000 Z
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 744804d982b..4fba9ed4c82 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -249,6 +249,10 @@ production: &base
repository_archive_cache_worker:
cron: "0 * * * *"
+ # Verify custom GitLab Pages domains
+ pages_domain_verification_cron_worker:
+ cron: "*/15 * * * *"
+
registry:
# enabled: true
# host: registry.example.com
diff --git a/config/initializers/0_as_concern.rb b/config/initializers/0_as_concern.rb
new file mode 100644
index 00000000000..40232bd6252
--- /dev/null
+++ b/config/initializers/0_as_concern.rb
@@ -0,0 +1,25 @@
+# This module is based on: https://gist.github.com/bcardarella/5735987
+
+module Prependable
+ def prepend_features(base)
+ if base.instance_variable_defined?(:@_dependencies)
+ base.instance_variable_get(:@_dependencies) << self
+ false
+ else
+ return false if base < self
+
+ super
+ base.singleton_class.send(:prepend, const_get('ClassMethods')) if const_defined?(:ClassMethods)
+ @_dependencies.each { |dep| base.send(:prepend, dep) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+ end
+end
+
+module ActiveSupport
+ module Concern
+ prepend Prependable
+
+ alias_method :prepended, :included
+ end
+end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 3c116501e4c..6cc90981cce 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -262,7 +262,7 @@ Settings.gitlab['signup_enabled'] ||= true if Settings.gitlab['signup_enabled'].
Settings.gitlab['signin_enabled'] ||= true if Settings.gitlab['signin_enabled'].nil?
Settings.gitlab['restricted_visibility_levels'] = Settings.__send__(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], [])
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
-Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?:, *| +and +)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
+Settings.gitlab['issue_closing_pattern'] = '((?:[Cc]los(?:e[sd]?|ing)|[Ff]ix(?:e[sd]|ing)?|[Rr]esolv(?:e[sd]?|ing)|[Ii]mplement(?:s|ed|ing)?)(:?) +(?:(?:issues? +)?%{issue_ref}(?:(?: *,? +and +| *, *)?)|([A-Z][A-Z0-9_]+-\d+))+)' if Settings.gitlab['issue_closing_pattern'].nil?
Settings.gitlab['default_projects_features'] ||= {}
Settings.gitlab['webhook_timeout'] ||= 10
Settings.gitlab['max_attachment_size'] ||= 10
@@ -446,6 +446,10 @@ Settings.cron_jobs['stuck_merge_jobs_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['stuck_merge_jobs_worker']['cron'] ||= '0 */2 * * *'
Settings.cron_jobs['stuck_merge_jobs_worker']['job_class'] = 'StuckMergeJobsWorker'
+Settings.cron_jobs['pages_domain_verification_cron_worker'] ||= Settingslogic.new({})
+Settings.cron_jobs['pages_domain_verification_cron_worker']['cron'] ||= '*/15 * * * *'
+Settings.cron_jobs['pages_domain_verification_cron_worker']['job_class'] = 'PagesDomainVerificationCronWorker'
+
#
# GitLab Shell
#
diff --git a/config/initializers/doorkeeper_openid_connect.rb b/config/initializers/doorkeeper_openid_connect.rb
index af174def047..98e1f6e830f 100644
--- a/config/initializers/doorkeeper_openid_connect.rb
+++ b/config/initializers/doorkeeper_openid_connect.rb
@@ -31,6 +31,7 @@ Doorkeeper::OpenidConnect.configure do
o.claim(:website) { |user| user.full_website_url if user.website_url? }
o.claim(:profile) { |user| Gitlab::Routing.url_helpers.user_url user }
o.claim(:picture) { |user| user.avatar_url(only_path: false) }
+ o.claim(:groups) { |user| user.membership_groups.map(&:full_path) }
end
end
end
diff --git a/config/initializers/flipper.rb b/config/initializers/flipper.rb
index cc9167d29b9..c60ad535fd5 100644
--- a/config/initializers/flipper.rb
+++ b/config/initializers/flipper.rb
@@ -8,7 +8,7 @@ Flipper.configure do |config|
cached_adapter = Flipper::Adapters::ActiveSupportCacheStore.new(
adapter,
Rails.cache,
- expires_in: 10.seconds)
+ expires_in: 1.hour)
Flipper.new(cached_adapter)
end
diff --git a/config/initializers/rack_attack_global.rb b/config/initializers/rack_attack_global.rb
index 9453df2ec5a..a90516eee7d 100644
--- a/config/initializers/rack_attack_global.rb
+++ b/config/initializers/rack_attack_global.rb
@@ -26,6 +26,7 @@ class Rack::Attack
throttle('throttle_unauthenticated', Gitlab::Throttle.unauthenticated_options) do |req|
Gitlab::Throttle.settings.throttle_unauthenticated_enabled &&
req.unauthenticated? &&
+ !req.api_internal_request? &&
req.ip
end
@@ -54,6 +55,10 @@ class Rack::Attack
path.start_with?('/api')
end
+ def api_internal_request?
+ path =~ %r{^/api/v\d+/internal/}
+ end
+
def web_request?
!api_request?
end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 0f164e628f9..161fb185c9b 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -10,7 +10,7 @@ Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS']
- chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS']
+ chain.add Gitlab::SidekiqMiddleware::Shutdown
chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware unless ENV['SIDEKIQ_REQUEST_STORE'] == '0'
chain.add Gitlab::SidekiqStatus::ServerMiddleware
end
diff --git a/config/karma.config.js b/config/karma.config.js
index a101d35704e..3d95e1622b2 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -24,7 +24,7 @@ module.exports = function(config) {
var karmaConfig = {
basePath: ROOT_PATH,
- browsers: ['ChromeHeadlessCustom'],
+ browsers: ['ChromeHeadlessCustom'],
customLaunchers: {
ChromeHeadlessCustom: {
base: 'ChromeHeadless',
diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml
index b1c71095d4f..889111282ef 100644
--- a/config/locales/doorkeeper.en.yml
+++ b/config/locales/doorkeeper.en.yml
@@ -68,7 +68,7 @@ en:
read_user:
Read-only access to the user's profile information, like username, public email and full name
openid:
- The ability to authenticate using GitLab, and read-only access to the user's profile information
+ The ability to authenticate using GitLab, and read-only access to the user's profile information and group memberships
sudo:
Access to the Sudo feature, to perform API actions as any user in the system (only available for admins)
flash:
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 1912808f9c0..8fe545b721e 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -55,7 +55,11 @@ constraints(ProjectUrlConstrainer.new) do
end
resource :pages, only: [:show, :destroy] do
- resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: %r{[^/]+} }
+ resources :domains, only: [:show, :new, :create, :destroy], controller: 'pages_domains', constraints: { id: %r{[^/]+} } do
+ member do
+ post :verify
+ end
+ end
end
resources :snippets, concerns: :awardable, constraints: { id: /\d+/ } do
@@ -74,7 +78,9 @@ constraints(ProjectUrlConstrainer.new) do
resource :mattermost, only: [:new, :create]
namespace :prometheus do
- get :active_metrics
+ resources :metrics, constraints: { id: %r{[^\/]+} }, only: [] do
+ get :active_common, on: :collection
+ end
end
resources :deploy_keys, constraints: { id: /\d+/ }, only: [:index, :new, :create, :edit, :update] do
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index eb49869e031..afea744eae6 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -68,3 +68,4 @@
- [gcp_cluster, 1]
- [project_migrate_hashed_storage, 1]
- [storage_migrator, 1]
+ - [pages_domain_verification, 1]
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 7f3fe551a03..98ba2edc0d1 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -25,17 +25,13 @@ var NO_COMPRESSION = process.env.NO_COMPRESSION;
var autoEntries = {};
var pageEntries = glob.sync('pages/**/index.js', { cwd: path.join(ROOT_PATH, 'app/assets/javascripts') });
-// filter out entries currently imported dynamically in dispatcher.js
-var dispatcher = fs.readFileSync(path.join(ROOT_PATH, 'app/assets/javascripts/dispatcher.js')).toString();
-var dispatcherChunks = dispatcher.match(/(?!import\('.\/)pages\/[^']+/g);
-
-pageEntries.forEach(( path ) => {
- let chunkPath = path.replace(/\/index\.js$/, '');
- if (!dispatcherChunks.includes(chunkPath)) {
- let chunkName = chunkPath.replace(/\//g, '.');
- autoEntries[chunkName] = './' + path;
- }
-});
+function generateAutoEntries(path, prefix = '.') {
+ const chunkPath = path.replace(/\/index\.js$/, '');
+ const chunkName = chunkPath.replace(/\//g, '.');
+ autoEntries[chunkName] = `${prefix}/${path}`;
+}
+
+pageEntries.forEach(( path ) => generateAutoEntries(path));
// report our auto-generated bundle count
var autoEntriesCount = Object.keys(autoEntries).length;
@@ -48,61 +44,44 @@ var config = {
},
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
entry: {
- account: './profile/account/index.js',
balsamiq_viewer: './blob/balsamiq_viewer.js',
- blob: './blob_edit/blob_bundle.js',
- boards: './boards/boards_bundle.js',
common: './commons/index.js',
common_vue: './vue_shared/vue_resource_interceptor.js',
cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
- commit_pipelines: './commit/pipelines/pipelines_bundle.js',
- deploy_keys: './deploy_keys/index.js',
- docs: './docs/docs_bundle.js',
diff_notes: './diff_notes/diff_notes_bundle.js',
environments: './environments/environments_bundle.js',
- environments_folder: './environments/folder/environments_folder_bundle.js',
filtered_search: './filtered_search/filtered_search_bundle.js',
- graphs: './graphs/graphs_bundle.js',
- graphs_charts: './graphs/graphs_charts.js',
- graphs_show: './graphs/graphs_show.js',
help: './help/help.js',
- how_to_merge: './how_to_merge.js',
- issue_show: './issue_show/index.js',
- integrations: './integrations',
- job_details: './jobs/job_details_bundle.js',
- locale: './locale/index.js',
- main: './main.js',
merge_conflicts: './merge_conflicts/merge_conflicts_bundle.js',
monitoring: './monitoring/monitoring_bundle.js',
network: './network/network_bundle.js',
notebook_viewer: './blob/notebook_viewer.js',
- notes: './notes/index.js',
pdf_viewer: './blob/pdf_viewer.js',
pipelines: './pipelines/pipelines_bundle.js',
- pipelines_charts: './pipelines/pipelines_charts.js',
pipelines_details: './pipelines/pipeline_details_bundle.js',
- pipelines_times: './pipelines/pipelines_times.js',
profile: './profile/profile_bundle.js',
project_import_gl: './projects/project_import_gitlab_project.js',
- prometheus_metrics: './prometheus_metrics',
protected_branches: './protected_branches',
protected_tags: './protected_tags',
registry_list: './registry/index.js',
- ide: './ide/index.js',
sidebar: './sidebar/sidebar_bundle.js',
- schedule_form: './pipeline_schedules/pipeline_schedule_form_bundle.js',
- schedules_index: './pipeline_schedules/pipeline_schedules_index_bundle.js',
snippet: './snippet/snippet_bundle.js',
sketch_viewer: './blob/sketch_viewer.js',
stl_viewer: './blob/stl_viewer.js',
terminal: './terminal/terminal_bundle.js',
- u2f: ['vendor/u2f'],
ui_development_kit: './ui_development_kit.js',
- raven: './raven/index.js',
vue_merge_request_widget: './vue_merge_request_widget/index.js',
- test: './test.js',
two_factor_auth: './two_factor_auth.js',
- users: './users/index.js',
+
+
+ common: './commons/index.js',
+ common_vue: './vue_shared/vue_resource_interceptor.js',
+ locale: './locale/index.js',
+ main: './main.js',
+ ide: './ide/index.js',
+ raven: './raven/index.js',
+ test: './test.js',
+ u2f: ['vendor/u2f'],
webpack_runtime: './webpack.js',
},
@@ -154,6 +133,27 @@ var config = {
}
},
{
+ test: /katex.css$/,
+ include: /node_modules\/katex\/dist/,
+ use: [
+ { loader: 'style-loader' },
+ {
+ loader: 'css-loader',
+ options: {
+ name: '[name].[hash].[ext]'
+ }
+ },
+ ],
+ },
+ {
+ test: /\.(eot|ttf|woff|woff2)$/,
+ include: /node_modules\/katex\/dist\/fonts/,
+ loader: 'file-loader',
+ options: {
+ name: '[name].[hash].[ext]',
+ }
+ },
+ {
test: /monaco-editor\/\w+\/vs\/loader\.js$/,
use: [
{ loader: 'exports-loader', options: 'l.global' },
@@ -236,20 +236,15 @@ var config = {
name: 'common_vue',
chunks: [
'boards',
- 'commit_pipelines',
'cycle_analytics',
'deploy_keys',
'diff_notes',
'environments',
- 'environments_folder',
'filtered_search',
'groups',
- 'issue_show',
- 'job_details',
'merge_conflicts',
'monitoring',
'notebook_viewer',
- 'notes',
'pdf_viewer',
'pipelines',
'pipelines_details',
@@ -265,20 +260,6 @@ var config = {
},
}),
- // create cacheable common library bundle for all d3 chunks
- new webpack.optimize.CommonsChunkPlugin({
- name: 'common_d3',
- chunks: [
- 'graphs',
- 'graphs_show',
- 'monitoring',
- 'users',
- ],
- minChunks: function (module, count) {
- return module.resource && /d3-/.test(module.resource);
- },
- }),
-
// create cacheable common library bundles
new webpack.optimize.CommonsChunkPlugin({
names: ['main', 'common', 'webpack_runtime'],
@@ -318,6 +299,7 @@ var config = {
'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'),
}
}
}
diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb
index 6f241f6fa4a..dfb50c195c1 100644
--- a/db/fixtures/development/01_admin.rb
+++ b/db/fixtures/development/01_admin.rb
@@ -9,7 +9,6 @@ Gitlab::Seeder.quiet do
s.username = 'root'
s.password = '5iveL!fe'
s.admin = true
- s.projects_limit = 100
s.confirmed_at = DateTime.now
end
end
diff --git a/db/migrate/20170929131201_populate_fork_networks.rb b/db/migrate/20170929131201_populate_fork_networks.rb
index 1214962770f..ddbf27e1852 100644
--- a/db/migrate/20170929131201_populate_fork_networks.rb
+++ b/db/migrate/20170929131201_populate_fork_networks.rb
@@ -6,22 +6,8 @@ class PopulateForkNetworks < ActiveRecord::Migration
DOWNTIME = false
- MIGRATION = 'PopulateForkNetworksRange'.freeze
- BATCH_SIZE = 100
- DELAY_INTERVAL = 15.seconds
-
- disable_ddl_transaction!
-
- class ForkedProjectLink < ActiveRecord::Base
- include EachBatch
-
- self.table_name = 'forked_project_links'
- end
-
def up
- say 'Populating the `fork_networks` based on existing `forked_project_links`'
-
- queue_background_migration_jobs_by_range_at_intervals(ForkedProjectLink, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
+ say 'Fork networks will be populated in 20171205190711 - RescheduleForkNetworkCreationCaller'
end
def down
diff --git a/db/migrate/20180119160751_optimize_ci_job_artifacts.rb b/db/migrate/20180119160751_optimize_ci_job_artifacts.rb
new file mode 100644
index 00000000000..9b4340ed7b7
--- /dev/null
+++ b/db/migrate/20180119160751_optimize_ci_job_artifacts.rb
@@ -0,0 +1,23 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class OptimizeCiJobArtifacts < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ # job_id is just here to be a covering index for index only scans
+ # since we'll almost always be joining against ci_builds on job_id
+ add_concurrent_index(:ci_job_artifacts, [:expire_at, :job_id])
+ add_concurrent_index(:ci_builds, [:artifacts_expire_at], where: "artifacts_file <> ''")
+ end
+
+ def down
+ remove_concurrent_index(:ci_job_artifacts, [:expire_at, :job_id])
+ remove_concurrent_index(:ci_builds, [:artifacts_expire_at], where: "artifacts_file <> ''")
+ end
+end
diff --git a/db/migrate/20180208183958_schedule_populate_untracked_uploads_if_needed.rb b/db/migrate/20180208183958_schedule_populate_untracked_uploads_if_needed.rb
new file mode 100644
index 00000000000..e46e793d9d2
--- /dev/null
+++ b/db/migrate/20180208183958_schedule_populate_untracked_uploads_if_needed.rb
@@ -0,0 +1,47 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class SchedulePopulateUntrackedUploadsIfNeeded < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ FOLLOW_UP_MIGRATION = 'PopulateUntrackedUploads'.freeze
+
+ class UntrackedFile < ActiveRecord::Base
+ include EachBatch
+
+ self.table_name = 'untracked_files_for_uploads'
+ end
+
+ def up
+ if table_exists?(:untracked_files_for_uploads)
+ process_or_remove_table
+ end
+ end
+
+ def down
+ # nothing
+ end
+
+ private
+
+ def process_or_remove_table
+ if UntrackedFile.all.empty?
+ drop_temp_table
+ else
+ schedule_populate_untracked_uploads_jobs
+ end
+ end
+
+ def drop_temp_table
+ drop_table(:untracked_files_for_uploads, if_exists: true)
+ end
+
+ def schedule_populate_untracked_uploads_jobs
+ say "Scheduling #{FOLLOW_UP_MIGRATION} background migration jobs since there are rows in untracked_files_for_uploads."
+
+ bulk_queue_background_migration_jobs_by_range(
+ UntrackedFile, FOLLOW_UP_MIGRATION)
+ end
+end
diff --git a/db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb b/db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb
new file mode 100644
index 00000000000..cedf2510dda
--- /dev/null
+++ b/db/migrate/20180213131630_add_partial_index_to_projects_for_index_only_scans.rb
@@ -0,0 +1,21 @@
+class AddPartialIndexToProjectsForIndexOnlyScans < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_projects_on_id_partial_for_visibility'
+
+ disable_ddl_transaction!
+
+ # Adds a partial index to leverage index-only scans when looking up project ids
+ def up
+ unless index_exists?(:projects, :id, name: INDEX_NAME)
+ add_concurrent_index :projects, :id, name: INDEX_NAME, unique: true, where: 'visibility_level IN (10,20)'
+ end
+ end
+
+ def down
+ if index_exists?(:projects, :id, name: INDEX_NAME)
+ remove_concurrent_index_by_name :projects, INDEX_NAME
+ end
+ end
+end
diff --git a/db/migrate/20180215181245_users_name_lower_index.rb b/db/migrate/20180215181245_users_name_lower_index.rb
new file mode 100644
index 00000000000..d3f68cb7d45
--- /dev/null
+++ b/db/migrate/20180215181245_users_name_lower_index.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class UsersNameLowerIndex < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+ INDEX_NAME = 'index_on_users_name_lower'
+
+ disable_ddl_transaction!
+
+ def up
+ return unless Gitlab::Database.postgresql?
+
+ # On GitLab.com this produces an index with a size of roughly 60 MB.
+ execute "CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON users (LOWER(name))"
+ end
+
+ def down
+ return unless Gitlab::Database.postgresql?
+
+ if supports_drop_index_concurrently?
+ execute "DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME}"
+ else
+ execute "DROP INDEX IF EXISTS #{INDEX_NAME}"
+ end
+ end
+end
diff --git a/db/migrate/20180216120000_add_pages_domain_verification.rb b/db/migrate/20180216120000_add_pages_domain_verification.rb
new file mode 100644
index 00000000000..8b7cae92285
--- /dev/null
+++ b/db/migrate/20180216120000_add_pages_domain_verification.rb
@@ -0,0 +1,8 @@
+class AddPagesDomainVerification < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :pages_domains, :verified_at, :datetime_with_timezone
+ add_column :pages_domains, :verification_code, :string
+ end
+end
diff --git a/db/migrate/20180216120010_add_pages_domain_verified_at_index.rb b/db/migrate/20180216120010_add_pages_domain_verified_at_index.rb
new file mode 100644
index 00000000000..825dfb52dce
--- /dev/null
+++ b/db/migrate/20180216120010_add_pages_domain_verified_at_index.rb
@@ -0,0 +1,15 @@
+class AddPagesDomainVerifiedAtIndex < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :pages_domains, :verified_at
+ end
+
+ def down
+ remove_concurrent_index :pages_domains, :verified_at
+ end
+end
diff --git a/db/migrate/20180216120020_allow_domain_verification_to_be_disabled.rb b/db/migrate/20180216120020_allow_domain_verification_to_be_disabled.rb
new file mode 100644
index 00000000000..06d458028b3
--- /dev/null
+++ b/db/migrate/20180216120020_allow_domain_verification_to_be_disabled.rb
@@ -0,0 +1,7 @@
+class AllowDomainVerificationToBeDisabled < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :application_settings, :pages_domain_verification_enabled, :boolean, default: true, null: false
+ end
+end
diff --git a/db/migrate/20180216120030_add_pages_domain_enabled_until.rb b/db/migrate/20180216120030_add_pages_domain_enabled_until.rb
new file mode 100644
index 00000000000..b40653044dd
--- /dev/null
+++ b/db/migrate/20180216120030_add_pages_domain_enabled_until.rb
@@ -0,0 +1,7 @@
+class AddPagesDomainEnabledUntil < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :pages_domains, :enabled_until, :datetime_with_timezone
+ end
+end
diff --git a/db/migrate/20180216120040_add_pages_domain_enabled_until_index.rb b/db/migrate/20180216120040_add_pages_domain_enabled_until_index.rb
new file mode 100644
index 00000000000..00f6e4979da
--- /dev/null
+++ b/db/migrate/20180216120040_add_pages_domain_enabled_until_index.rb
@@ -0,0 +1,17 @@
+class AddPagesDomainEnabledUntilIndex < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :pages_domains, [:project_id, :enabled_until]
+ add_concurrent_index :pages_domains, [:verified_at, :enabled_until]
+ end
+
+ def down
+ remove_concurrent_index :pages_domains, [:verified_at, :enabled_until]
+ remove_concurrent_index :pages_domains, [:project_id, :enabled_until]
+ end
+end
diff --git a/db/migrate/20180216120050_pages_domains_verification_grace_period.rb b/db/migrate/20180216120050_pages_domains_verification_grace_period.rb
new file mode 100644
index 00000000000..d7f8634b536
--- /dev/null
+++ b/db/migrate/20180216120050_pages_domains_verification_grace_period.rb
@@ -0,0 +1,26 @@
+class PagesDomainsVerificationGracePeriod < ActiveRecord::Migration
+ DOWNTIME = false
+
+ class PagesDomain < ActiveRecord::Base
+ include EachBatch
+ end
+
+ # Allow this migration to resume if it fails partway through
+ disable_ddl_transaction!
+
+ def up
+ now = Time.now
+ grace = now + 30.days
+
+ PagesDomain.each_batch do |relation|
+ relation.update_all(verified_at: now, enabled_until: grace)
+
+ # Sleep 2 minutes between batches to not overload the DB with dead tuples
+ sleep(2.minutes) unless relation.reorder(:id).last == PagesDomain.reorder(:id).last
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
index 26b99b61424..c48f1c938d0 100644
--- a/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
+++ b/db/post_migrate/20170717111152_cleanup_move_system_upload_folder_symlink.rb
@@ -20,7 +20,7 @@ class CleanupMoveSystemUploadFolderSymlink < ActiveRecord::Migration
def down
if File.directory?(new_directory)
say "Symlinking #{old_directory} -> #{new_directory}"
- FileUtils.ln_s(new_directory, old_directory)
+ FileUtils.ln_s(new_directory, old_directory) unless File.exist?(old_directory)
else
say "#{new_directory} doesn't exist, skipping."
end
diff --git a/db/post_migrate/20171124150326_reschedule_fork_network_creation.rb b/db/post_migrate/20171124150326_reschedule_fork_network_creation.rb
index 05430efe1f6..26f917d5a1e 100644
--- a/db/post_migrate/20171124150326_reschedule_fork_network_creation.rb
+++ b/db/post_migrate/20171124150326_reschedule_fork_network_creation.rb
@@ -3,22 +3,8 @@ class RescheduleForkNetworkCreation < ActiveRecord::Migration
DOWNTIME = false
- MIGRATION = 'PopulateForkNetworksRange'.freeze
- BATCH_SIZE = 100
- DELAY_INTERVAL = 15.seconds
-
- disable_ddl_transaction!
-
- class ForkedProjectLink < ActiveRecord::Base
- include EachBatch
-
- self.table_name = 'forked_project_links'
- end
-
def up
- say 'Populating the `fork_networks` based on existing `forked_project_links`'
-
- queue_background_migration_jobs_by_range_at_intervals(ForkedProjectLink, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
+ say 'Fork networks will be populated in 20171205190711 - RescheduleForkNetworkCreationCaller'
end
def down
diff --git a/db/post_migrate/20180216121020_fill_pages_domain_verification_code.rb b/db/post_migrate/20180216121020_fill_pages_domain_verification_code.rb
new file mode 100644
index 00000000000..d423673d2a5
--- /dev/null
+++ b/db/post_migrate/20180216121020_fill_pages_domain_verification_code.rb
@@ -0,0 +1,41 @@
+class FillPagesDomainVerificationCode < ActiveRecord::Migration
+ DOWNTIME = false
+
+ class PagesDomain < ActiveRecord::Base
+ include EachBatch
+ end
+
+ # Allow this migration to resume if it fails partway through
+ disable_ddl_transaction!
+
+ def up
+ PagesDomain.where(verification_code: [nil, '']).each_batch do |relation|
+ connection.execute(set_codes_sql(relation))
+
+ # Sleep 2 minutes between batches to not overload the DB with dead tuples
+ sleep(2.minutes) unless relation.reorder(:id).last == PagesDomain.reorder(:id).last
+ end
+
+ change_column_null(:pages_domains, :verification_code, false)
+ end
+
+ def down
+ change_column_null(:pages_domains, :verification_code, true)
+ end
+
+ private
+
+ def set_codes_sql(relation)
+ ids = relation.pluck(:id)
+ whens = ids.map { |id| "WHEN #{id} THEN '#{SecureRandom.hex(16)}'" }
+
+ <<~SQL
+ UPDATE pages_domains
+ SET verification_code =
+ CASE id
+ #{whens.join("\n")}
+ END
+ WHERE id IN(#{ids.join(',')})
+ SQL
+ end
+end
diff --git a/db/post_migrate/20180216121030_enqueue_verify_pages_domain_workers.rb b/db/post_migrate/20180216121030_enqueue_verify_pages_domain_workers.rb
new file mode 100644
index 00000000000..bf9bf4e660f
--- /dev/null
+++ b/db/post_migrate/20180216121030_enqueue_verify_pages_domain_workers.rb
@@ -0,0 +1,16 @@
+class EnqueueVerifyPagesDomainWorkers < ActiveRecord::Migration
+ class PagesDomain < ActiveRecord::Base
+ include EachBatch
+ end
+
+ def up
+ PagesDomain.each_batch do |relation|
+ ids = relation.pluck(:id).map { |id| [id] }
+ PagesDomainVerificationWorker.bulk_perform_async(ids)
+ end
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index e381fc9fb8a..fba0c710705 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20180206200543) do
+ActiveRecord::Schema.define(version: 20180216121030) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -156,6 +156,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.integer "gitaly_timeout_fast", default: 10, null: false
t.boolean "authorized_keys_enabled", default: true, null: false
t.string "auto_devops_domain"
+ t.boolean "pages_domain_verification_enabled", default: true, null: false
end
create_table "audit_events", force: :cascade do |t|
@@ -295,6 +296,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.integer "failure_reason"
end
+ add_index "ci_builds", ["artifacts_expire_at"], name: "index_ci_builds_on_artifacts_expire_at", where: "(artifacts_file <> ''::text)", using: :btree
add_index "ci_builds", ["auto_canceled_by_id"], name: "index_ci_builds_on_auto_canceled_by_id", using: :btree
add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
add_index "ci_builds", ["commit_id", "status", "type"], name: "index_ci_builds_on_commit_id_and_status_and_type", using: :btree
@@ -336,6 +338,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.string "file"
end
+ add_index "ci_job_artifacts", ["expire_at", "job_id"], name: "index_ci_job_artifacts_on_expire_at_and_job_id", using: :btree
add_index "ci_job_artifacts", ["job_id", "file_type"], name: "index_ci_job_artifacts_on_job_id_and_file_type", unique: true, using: :btree
add_index "ci_job_artifacts", ["project_id"], name: "index_ci_job_artifacts_on_project_id", using: :btree
@@ -1315,10 +1318,16 @@ ActiveRecord::Schema.define(version: 20180206200543) do
t.string "encrypted_key_iv"
t.string "encrypted_key_salt"
t.string "domain"
+ t.datetime_with_timezone "verified_at"
+ t.string "verification_code", null: false
+ t.datetime_with_timezone "enabled_until"
end
add_index "pages_domains", ["domain"], name: "index_pages_domains_on_domain", unique: true, using: :btree
+ add_index "pages_domains", ["project_id", "enabled_until"], name: "index_pages_domains_on_project_id_and_enabled_until", using: :btree
add_index "pages_domains", ["project_id"], name: "index_pages_domains_on_project_id", using: :btree
+ add_index "pages_domains", ["verified_at", "enabled_until"], name: "index_pages_domains_on_verified_at_and_enabled_until", using: :btree
+ add_index "pages_domains", ["verified_at"], name: "index_pages_domains_on_verified_at", using: :btree
create_table "personal_access_tokens", force: :cascade do |t|
t.integer "user_id", null: false
@@ -1470,6 +1479,7 @@ ActiveRecord::Schema.define(version: 20180206200543) do
add_index "projects", ["created_at"], name: "index_projects_on_created_at", using: :btree
add_index "projects", ["creator_id"], name: "index_projects_on_creator_id", using: :btree
add_index "projects", ["description"], name: "index_projects_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"}
+ add_index "projects", ["id"], name: "index_projects_on_id_partial_for_visibility", unique: true, where: "(visibility_level = ANY (ARRAY[10, 20]))", using: :btree
add_index "projects", ["last_activity_at"], name: "index_projects_on_last_activity_at", using: :btree
add_index "projects", ["last_repository_check_failed"], name: "index_projects_on_last_repository_check_failed", using: :btree
add_index "projects", ["last_repository_updated_at"], name: "index_projects_on_last_repository_updated_at", using: :btree
diff --git a/doc/README.md b/doc/README.md
index c8b6b4f32b8..46fcb7c6baf 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -12,6 +12,10 @@ GitLab offers the most scalable Git-based fully integrated platform for software
With GitLab self-hosted, you deploy your own GitLab instance on-premises or on a private cloud of your choice. GitLab self-hosted is available for [free and with paid subscriptions](https://about.gitlab.com/products/): Libre, Starter, Premium, and Ultimate.
+Every feature available in Libre is also available in Starter, Premium, and Ultimate.
+Starter features are also available in Premium and Ultimate, and Premium features are also
+available in Ultimate.
+
GitLab.com is our SaaS offering. It's hosted, managed, and administered by GitLab, with [free and paid plans](https://about.gitlab.com/gitlab-com/) for individuals and teams: Free, Bronze, Silver, and Gold.
## Shortcuts to GitLab's most visited docs
@@ -124,8 +128,8 @@ Manage your [repositories](user/project/repository/index.md) from the UI (user i
## Administrator documentation
-[Administration documentation](administration/index.md) applies to admin users of [GitLab
-self-hosted instances](#self-hosted-gitlab): Libre, Starter, Premium, Ultimate.
+[Administration documentation](administration/index.md) applies to admin users of GitLab
+self-hosted instances.
Learn how to install, configure, update, upgrade, integrate, and maintain your own instance.
Regular users don't have access to GitLab administration tools and settings.
@@ -133,7 +137,7 @@ Regular users don't have access to GitLab administration tools and settings.
## Contributor documentation
GitLab Community Edition is [open source](https://gitlab.com/gitlab-org/gitlab-ce/)
-and Enterprise Editions are [open-core](https://gitlab.com/gitlab-org/gitlab-ee/).
+and GitLab Enterprise Edition is [open-core](https://gitlab.com/gitlab-org/gitlab-ee/).
Learn how to contribute to GitLab:
- [Development](development/README.md): All styleguides and explanations how to contribute.
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 9b9b8ca89e5..aa5e9513290 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
@@ -1,9 +1,12 @@
-# How to configure LDAP with GitLab CE
+---
+author: Chris Wilson
+author_gitlab: MrChrisW
+level: intermediary
+article_type: admin guide
+date: 2017-05-03
+---
-> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** admin guide ||
-> **Level:** intermediary ||
-> **Author:** [Chris Wilson](https://gitlab.com/MrChrisW) ||
-> **Publication date:** 2017-05-03
+# How to configure LDAP with GitLab CE
## Introduction
diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md
index 4d35b20d0c3..960970aea30 100644
--- a/doc/administration/custom_hooks.md
+++ b/doc/administration/custom_hooks.md
@@ -4,8 +4,9 @@
**Note:** Custom Git hooks must be configured on the filesystem of the GitLab
server. Only GitLab server administrators will be able to complete these tasks.
Please explore [webhooks] as an option if you do not
-have filesystem access. For a user configurable Git hook interface, please see
-[GitLab Enterprise Edition Git Hooks](http://docs.gitlab.com/ee/git_hooks/git_hooks.html).
+have filesystem access. For a user configurable Git hook interface, see
+[Push Rules](https://docs.gitlab.com/ee/push_rules/push_rules.html),
+available in GitLab Enterprise Edition.
Git natively supports hooks that are executed on different actions.
Examples of server-side git hooks include pre-receive, post-receive, and update.
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index e3b10119090..d9a61aea6ef 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -2,7 +2,9 @@
[Gitaly](https://gitlab.com/gitlab-org/gitaly) (introduced in GitLab
9.0) is a service that provides high-level RPC access to Git
-repositories. Gitaly is a mandatory component in GitLab 9.4 and newer.
+repositories. Gitaly was optional when it was first introduced in
+GitLab, but since GitLab 9.4 it is a mandatory component of the
+application.
GitLab components that access Git repositories (gitlab-rails,
gitlab-shell, gitlab-workhorse) act as clients to Gitaly. End users do
@@ -184,14 +186,20 @@ Gitaly logs on your Gitaly server (`sudo gitlab-ctl tail gitaly` or
coming in. One sure way to trigger a Gitaly request is to clone a
repository from your GitLab server over HTTP.
-## Disabling or enabling the Gitaly service
+## Disabling or enabling the Gitaly service in a cluster environment
If you are running Gitaly [as a remote
service](#running-gitaly-on-its-own-server) you may want to disable
the local Gitaly service that runs on your Gitlab server by default.
-To disable the Gitaly service in your Omnibus installation, add the
-following line to `/etc/gitlab/gitlab.rb`:
+> 'Disabling Gitaly' only makes sense when you run GitLab in a custom
+cluster configuration, where different services run on different
+machines. Disabling Gitaly on all machines in the cluster is not a
+valid configuration.
+
+If you are setting up a GitLab cluster where Gitaly does not need to
+run on all machines, you can disable the Gitaly service in your
+Omnibus installation, add the following line to `/etc/gitlab/gitlab.rb`:
```ruby
gitaly['enable'] = false
@@ -200,11 +208,13 @@ gitaly['enable'] = false
When you run `gitlab-ctl reconfigure` the Gitaly service will be
disabled.
-To disable the Gitaly service in an installation from source, add the
-following to `/etc/default/gitlab`:
+To disable the Gitaly service in a GitLab cluster where you installed
+GitLab from source, add the following to `/etc/default/gitlab` on the
+machine where you want to disable Gitaly.
```shell
gitaly_enabled=false
```
-When you run `service gitlab restart` Gitaly will be disabled.
+When you run `service gitlab restart` Gitaly will be disabled on this
+particular machine.
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index b85a166089d..e201848791c 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -25,11 +25,11 @@ for each GitLab application server in your environment.
options. Here is an example snippet to add to `/etc/fstab`:
```
- 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
- 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
- 10.1.1.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nobootwait,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/.ssh /var/opt/gitlab/.ssh nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/gitlab-rails/uploads /var/opt/gitlab/gitlab-rails/uploads nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/gitlab-rails/shared /var/opt/gitlab/gitlab-rails/shared nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/gitlab-ci/builds /var/opt/gitlab/gitlab-ci/builds nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
+ 10.1.0.1:/var/opt/gitlab/git-data /var/opt/gitlab/git-data nfs defaults,soft,rsize=1048576,wsize=1048576,noatime,nofail,lookupcache=positive 0 2
```
1. Create the shared directories. These may be different depending on your NFS
diff --git a/doc/administration/index.md b/doc/administration/index.md
index e53268e5f3e..51444651bdb 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -1,9 +1,19 @@
# Administrator documentation
Learn how to administer your GitLab instance (Community Edition and
-[Enterprise Editions](https://about.gitlab.com/products/)).
+Enterprise Edition).
Regular users don't have access to GitLab administration tools and settings.
+GitLab has two product distributions: the open source
+[GitLab Community Edition (CE)](https://gitlab.com/gitlab-org/gitlab-ce),
+and the open core [GitLab Enterprise Edition (EE)](https://gitlab.com/gitlab-org/gitlab-ee),
+available through [different subscriptions](https://about.gitlab.com/products/).
+
+You can [install GitLab CE or GitLab EE](https://about.gitlab.com/installation/ce-or-ee/),
+but the features you'll have access to depend on the subscription you choose
+(Libre, Starter, Premium, or Ultimate). GitLab Community Edition installations
+only have access to Libre features.
+
GitLab.com is administered by GitLab, Inc., therefore, only GitLab team members have
access to its admin configurations. If you're a GitLab.com user, please check the
[user documentation](../user/index.html).
diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md
index 65f59b72690..d978d1dca53 100644
--- a/doc/administration/integration/plantuml.md
+++ b/doc/administration/integration/plantuml.md
@@ -9,7 +9,19 @@ created in snippets, wikis, and repos.
## PlantUML Server
Before you can enable PlantUML in GitLab; you need to set up your own PlantUML
-server that will generate the diagrams. Installing and configuring your
+server that will generate the diagrams.
+
+### Docker
+
+With Docker, you can just run a container like this:
+
+`docker run -d --name plantuml -p 8080:8080 plantuml/plantuml-server:tomcat`
+
+The **PlantUML URL** will be the hostname of the server running the container.
+
+### Debian/Ubuntu
+
+Installing and configuring your
own PlantUML server is easy in Debian/Ubuntu distributions using Tomcat.
First you need to create a `plantuml.war` file from the source code:
diff --git a/doc/administration/logs.md b/doc/administration/logs.md
index debaa2330d0..1b42d7979ed 100644
--- a/doc/administration/logs.md
+++ b/doc/administration/logs.md
@@ -32,6 +32,8 @@ In this example, you can see this was a GET request for a specific issue. Notice
2. `view`: total time taken inside the Rails views
3. `db`: total time to retrieve data from the database
+User clone/fetch activity using http transport appears in this log as `action: git_upload_pack`.
+
In addition, the log contains the IP address from which the request originated
(`remote_ip`) as well as the user's ID (`user_id`), and username (`username`).
@@ -157,6 +159,8 @@ I, [2015-02-13T06:17:00.671315 #9291] INFO -- : Adding project root/example.git
I, [2015-02-13T06:17:00.679433 #9291] INFO -- : Moving existing hooks directory and symlinking global hooks directory for /var/opt/gitlab/git-data/repositories/root/example.git.
```
+User clone/fetch activity using ssh transport appears in this log as `executing git command <gitaly-upload-pack...`.
+
## `unicorn\_stderr.log`
This file lives in `/var/log/gitlab/unicorn/unicorn_stderr.log` for
diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md
index d6333ee62b4..b6320aba83e 100644
--- a/doc/administration/monitoring/index.md
+++ b/doc/administration/monitoring/index.md
@@ -5,5 +5,5 @@ Explore our features to monitor your GitLab instance:
- [Performance monitoring](performance/index.md): GitLab Performance Monitoring makes it possible to measure a wide variety of statistics of your instance.
- [Prometheus](prometheus/index.md): Prometheus is a powerful time-series monitoring service, providing a flexible platform for monitoring GitLab and other software products.
- [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics.
-- [Monitoring uptime](../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
+- [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.
diff --git a/doc/administration/operations/fast_ssh_key_lookup.md b/doc/administration/operations/fast_ssh_key_lookup.md
index 9d1589d84aa..bd6c7bb07b5 100644
--- a/doc/administration/operations/fast_ssh_key_lookup.md
+++ b/doc/administration/operations/fast_ssh_key_lookup.md
@@ -1,7 +1,7 @@
# Fast lookup of authorized SSH keys in the database
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/1631) in
-> [GitLab Enterprise Edition Standard](https://about.gitlab.com/gitlab-ee) 9.3.
+> [GitLab Starter](https://about.gitlab.com/gitlab-ee) 9.3.
>
> [Available in](https://gitlab.com/gitlab-org/gitlab-ee/issues/3953) GitLab
> Community Edition 10.4.
@@ -56,7 +56,7 @@ new one, and attempting to pull a repo.
> **Warning:** Do not disable writes until SSH is confirmed to be working
perfectly, because the file will quickly become out-of-date.
-In the case of lookup failures (which are not uncommon), the `authorized_keys`
+In the case of lookup failures (which are common), the `authorized_keys`
file will still be scanned. So git SSH performance will still be slow for many
users as long as a large file exists.
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 7d47aaac299..00c631fdaae 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -61,6 +61,21 @@ Before proceeding with the Pages configuration, you will need to:
NOTE: **Note:**
If your GitLab instance and the Pages daemon are deployed in a private network or behind a firewall, your GitLab Pages websites will only be accessible to devices/users that have access to the private network.
+### Add the domain to the Public Suffix List
+
+The [Public Suffix List](https://publicsuffix.org) is used by browsers to
+decide how to treat subdomains. If your GitLab instance allows members of the
+public to create GitLab Pages sites, it also allows those users to create
+subdomains on the pages domain (`example.io`). Adding the domain to the Public
+Suffix List prevents browsers from accepting
+[supercookies](https://en.wikipedia.org/wiki/HTTP_cookie#Supercookie),
+among other things.
+
+Follow [these instructions](https://publicsuffix.org/submit/) to submit your
+GitLab Pages subdomain. For instance, if your domain is `example.io`, you should
+request that `*.example.io` is added to the Public Suffix List. GitLab.com
+added `*.gitlab.io` [in 2016](https://gitlab.com/gitlab-com/infrastructure/issues/230).
+
### DNS configuration
GitLab Pages expect to run on their own virtual host. In your DNS server/provider
@@ -211,6 +226,18 @@ world. Custom domains and TLS are supported.
1. [Reconfigure GitLab][reconfigure]
+### Custom domain verification
+
+To prevent malicious users from hijacking domains that don't belong to them,
+GitLab supports [custom domain verification](../../user/project/pages/getting_started_part_three.md#dns-txt-record).
+When adding a custom domain, users will be required to prove they own it by
+adding a GitLab-controlled verification code to the DNS records for that domain.
+
+If your userbase is private or otherwise trusted, you can disable the
+verification requirement. Navigate to `Admin area âž” Settings` and uncheck
+**Require users to prove ownership of custom domains** in the Pages section.
+This setting is enabled by default.
+
## Change storage path
Follow the steps below to change the default path where GitLab Pages' contents
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index c5b286f6804..39bd19ac851 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -4,50 +4,63 @@
## Legacy Storage
-Legacy Storage is the storage behavior prior to version 10.0. For historical reasons, GitLab replicated the same
-mapping structure from the projects URLs:
+Legacy Storage is the storage behavior prior to version 10.0. For historical
+reasons, GitLab replicated the same mapping structure from the projects URLs:
- * Project's repository: `#{namespace}/#{project_name}.git`
- * Project's wiki: `#{namespace}/#{project_name}.wiki.git`
+* Project's repository: `#{namespace}/#{project_name}.git`
+* Project's wiki: `#{namespace}/#{project_name}.wiki.git`
-This structure made simple to migrate from existing solutions to GitLab and easy for Administrators to find where the
-repository is stored.
+This structure made it simple to migrate from existing solutions to GitLab and
+easy for Administrators to find where the repository is stored.
On the other hand this has some drawbacks:
-Storage location will concentrate huge amount of top-level namespaces. The impact can be reduced by the introduction of [multiple storage paths][storage-paths].
+Storage location will concentrate huge amount of top-level namespaces. The
+impact can be reduced by the introduction of [multiple storage
+paths][storage-paths].
-Because Backups are a snapshot of the same URL mapping, if you try to recover a very old backup, you need to verify
-if any project has taken the place of an old removed project sharing the same URL. This means that `mygroup/myproject`
-from your backup may not be the same original project that is today in the same URL.
+Because backups are a snapshot of the same URL mapping, if you try to recover a
+very old backup, you need to verify whether any project has taken the place of
+an old removed or renamed project sharing the same URL. This means that
+`mygroup/myproject` from your backup may not be the same original project that
+is at that same URL today.
-Any change in the URL will need to be reflected on disk (when groups / users or projects are renamed). This can add a lot
-of load in big installations, and can be even worst if they are using any type of network based filesystem.
+Any change in the URL will need to be reflected on disk (when groups / users or
+projects are renamed). This can add a lot of load in big installations,
+especially if using any type of network based filesystem.
-Last, for GitLab Geo, this storage type means we have to synchronize the disk state, replicate renames in the correct
-order or we may end-up with wrong repository or missing data temporarily.
+For GitLab Geo in particular: Geo does work with legacy storage, but in some
+edge cases due to race conditions it can lead to errors when a project is
+renamed multiple times in short succession, or a project is deleted and
+recreated under the same name very quickly. We expect these race events to be
+rare, and we have not observed a race condition side-effect happening yet.
-This pattern also exists in other objects stored in GitLab, like issue Attachments, GitLab Pages artifacts,
-Docker Containers for the integrated Registry, etc.
+This pattern also exists in other objects stored in GitLab, like issue
+Attachments, GitLab Pages artifacts, Docker Containers for the integrated
+Registry, etc.
## Hashed Storage
-Hashed Storage is the new storage behavior we are rolling out with 10.0. It's not enabled by default yet, but we
-encourage everyone to try-it and take the time to fix any script you may have that depends on the old behavior.
+> **Warning:** Hashed storage is in **Beta**. For the latest updates, check the
+> associated [issue](https://gitlab.com/gitlab-com/infrastructure/issues/2821)
+> and please report any problems you encounter.
-Instead of coupling project URL and the folder structure where the repository will be stored on disk, we are coupling
-a hash, based on the project's ID.
+Hashed Storage is the new storage behavior we are rolling out with 10.0. Instead
+of coupling project URL and the folder structure where the repository will be
+stored on disk, we are coupling a hash, based on the project's ID. This makes
+the folder structure immutable, and therefore eliminates any requirement to
+synchronize state from URLs to disk structure. This means that renaming a group,
+user, or project will cost only the database transaction, and will take effect
+immediately.
-This makes the folder structure immutable, and therefore eliminates any requirement to synchronize state from URLs to
-disk structure. This means that renaming a group, user or project will cost only the database transaction, and will take
-effect immediately.
+The hash also helps to spread the repositories more evenly on the disk, so the
+top-level directory will contain less folders than the total amount of top-level
+namespaces.
-The hash also helps to spread the repositories more evenly on the disk, so the top-level directory will contain less
-folders than the total amount of top-level namespaces.
-
-Hash format is based on hexadecimal representation of SHA256: `SHA256(project.id)`.
-Top-level folder uses first 2 characters, followed by another folder with the next 2 characters. They are both stored in
-a special folder `@hashed`, to co-exist with existing Legacy projects:
+The hash format is based on the hexadecimal representation of SHA256:
+`SHA256(project.id)`. The top-level folder uses the first 2 characters, followed
+by another folder with the next 2 characters. They are both stored in a special
+`@hashed` folder, to be able to co-exist with existing Legacy Storage projects:
```ruby
# Project's repository:
@@ -57,15 +70,13 @@ a special folder `@hashed`, to co-exist with existing Legacy projects:
"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git"
```
-This new format also makes possible to restore backups with confidence, as when restoring a repository from the backup,
-you will never mistakenly restore a repository in the wrong project (considering the backup is made after the migration).
-
### How to migrate to Hashed Storage
-In GitLab, go to **Admin > Settings**, find the **Repository Storage** section and select
-"_Create new projects using hashed storage paths_".
+In GitLab, go to **Admin > Settings**, find the **Repository Storage** section
+and select "_Create new projects using hashed storage paths_".
-To migrate your existing projects to the new storage type, check the specific [rake tasks].
+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
@@ -73,11 +84,13 @@ To migrate your existing projects to the new storage type, check the specific [r
### 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.
+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)).
-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}`, which is true for CI Cache and LFS Objects.
+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}`,
+which is true for CI Cache and LFS Objects.
| Storable Object | Legacy Storage | Hashed Storage | S3 Compatible | GitLab Version |
| --------------- | -------------- | -------------- | ------------- | -------------- |
diff --git a/doc/api/README.md b/doc/api/README.md
index f226716c3b5..b193ef4ab7f 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -43,12 +43,14 @@ following locations:
- [Pipeline Schedules](pipeline_schedules.md)
- [Projects](projects.md) including setting Webhooks
- [Project Access Requests](access_requests.md)
+- [Project import/export](project_import_export.md)
- [Project Members](members.md)
- [Project Snippets](project_snippets.md)
- [Protected Branches](protected_branches.md)
- [Repositories](repositories.md)
- [Repository Files](repository_files.md)
- [Runners](runners.md)
+- [Search](search.md)
- [Services](services.md)
- [Settings](settings.md)
- [Sidekiq metrics](sidekiq_metrics.md)
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 63554c63057..2c745d00887 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -198,6 +198,41 @@ Example response:
}
```
+## Get references a commit is pushed to
+
+> [Introduced][ce-15026] in GitLab 10.6
+
+Get all references (from branches or tags) a commit is pushed to.
+The pagination parameters `page` and `per_page` can be used to restrict the list of references.
+
+```
+GET /projects/:id/repository/commits/:sha/refs
+```
+
+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
+| `sha` | string | yes | The commit hash |
+| `type` | string | no | The scope of commits. Possible values `branch`, `tag`, `all`. Default is `all`. |
+
+```bash
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/5/repository/commits/5937ac0a7beb003549fc5fd26fc247adbce4a52e/refs?type=all"
+```
+
+Example response:
+
+```json
+[
+ {"type": "branch", "name": "'test'"},
+ {"type": "branch", "name": "add-balsamiq-file"},
+ {"type": "branch", "name": "wip"},
+ {"type": "tag", "name": "v1.1.0"}
+ ]
+
+```
+
## Cherry pick a commit
> [Introduced][ce-8047] in GitLab 8.15.
@@ -500,3 +535,4 @@ Example response:
[ce-6096]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6096 "Multi-file commit"
[ce-8047]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8047
+[ce-15026]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/15026
diff --git a/doc/api/groups.md b/doc/api/groups.md
index de730cdd869..f50558b58a6 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -15,6 +15,7 @@ Parameters:
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `owned` | boolean | no | Limit to groups owned by the current user |
```
@@ -98,6 +99,7 @@ Parameters:
| `order_by` | string | no | Order groups by `name` or `path`. Default is `name` |
| `sort` | string | no | Order groups in `asc` or `desc` order. Default is `asc` |
| `statistics` | boolean | no | Include group statistics (admins only) |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `owned` | boolean | no | Limit to groups owned by the current user |
```
@@ -145,6 +147,7 @@ Parameters:
| `simple` | boolean | no | Return only the ID, URL, name, and path of each project |
| `owned` | boolean | no | Limit by projects owned by the current user |
| `starred` | boolean | no | Limit by projects starred by the current user |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
Example response:
@@ -204,6 +207,7 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the group](README.md#namespaced-path-encoding) owned by the authenticated user |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
```bash
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/groups/4
diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md
index 25cae5ce1f9..1f0dc700640 100644
--- a/doc/api/namespaces.md
+++ b/doc/api/namespaces.md
@@ -39,7 +39,7 @@ Example response:
"path": "group1",
"kind": "group",
"full_path": "group1",
- "parent_id": "null",
+ "parent_id": null,
"members_count_with_descendants": 2
},
{
@@ -48,7 +48,7 @@ Example response:
"path": "bar",
"kind": "group",
"full_path": "foo/bar",
- "parent_id": "9",
+ "parent_id": 9,
"members_count_with_descendants": 5
}
]
@@ -84,7 +84,7 @@ Example response:
"path": "twitter",
"kind": "group",
"full_path": "twitter",
- "parent_id": "null",
+ "parent_id": null,
"members_count_with_descendants": 2
}
]
@@ -117,7 +117,7 @@ Example response:
"path": "group1",
"kind": "group",
"full_path": "group1",
- "parent_id": "null",
+ "parent_id": null,
"members_count_with_descendants": 2
}
```
@@ -137,7 +137,7 @@ Example response:
"path": "group1",
"kind": "group",
"full_path": "group1",
- "parent_id": "null",
+ "parent_id": null,
"members_count_with_descendants": 2
}
```
diff --git a/doc/api/project_import_export.md b/doc/api/project_import_export.md
new file mode 100644
index 00000000000..e442442c750
--- /dev/null
+++ b/doc/api/project_import_export.md
@@ -0,0 +1,74 @@
+# Project import API
+
+[Introduced][ce-41899] in GitLab 10.6
+
+[See also the project import/export documentation](../user/project/settings/import_export.md)
+
+## Import a file
+
+```http
+POST /projects/import
+```
+
+| Attribute | Type | Required | Description |
+| --------- | -------------- | -------- | ---------------------------------------- |
+| `namespace` | integer/string | no | The ID or path of the namespace that the project will be imported to. Defaults to the current user's namespace |
+| `file` | string | yes | The file to be uploaded |
+| `path` | string | yes | Name and path for new project |
+
+To upload a file from your filesystem, use the `--form` argument. This causes
+cURL to post data using the header `Content-Type: multipart/form-data`.
+The `file=` parameter must point to a file on your filesystem and be preceded
+by `@`. For example:
+
+```console
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --form "path=api-project" --form "file=@/path/to/file" https://gitlab.example.com/api/v4/projects/import
+```
+
+```json
+{
+ "id": 1,
+ "description": null,
+ "name": "api-project",
+ "name_with_namespace": "Administrator / api-project",
+ "path": "api-project",
+ "path_with_namespace": "root/api-project",
+ "created_at": "2018-02-13T09:05:58.023Z",
+ "import_status": "scheduled"
+}
+```
+
+## Import status
+
+Get the status of an import.
+
+```http
+GET /projects/:id/import
+```
+
+| 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 |
+
+```console
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/projects/1/import
+```
+
+Status can be one of `none`, `scheduled`, `failed`, `started`, or `finished`.
+
+If the status is `failed`, it will include the import error message under `import_error`.
+
+```json
+{
+ "id": 1,
+ "description": "Itaque perspiciatis minima aspernatur corporis consequatur.",
+ "name": "Gitlab Test",
+ "name_with_namespace": "Gitlab Org / Gitlab Test",
+ "path": "gitlab-test",
+ "path_with_namespace": "gitlab-org/gitlab-test",
+ "created_at": "2017-08-29T04:36:44.383Z",
+ "import_status": "started"
+}
+```
+
+[ce-41899]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41899
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 46f5de5aa0e..4f4ab906c1a 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -37,6 +37,7 @@ GET /projects
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
@@ -220,6 +221,7 @@ GET /users/:user_id/projects
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
@@ -388,6 +390,7 @@ GET /projects/:id
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `statistics` | boolean | no | Include project statistics |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
```json
{
@@ -664,6 +667,7 @@ GET /projects/:id/forks
| `membership` | boolean | no | Limit by projects that the current user is a member of |
| `starred` | boolean | no | Limit by projects starred by the current user |
| `statistics` | boolean | no | Include project statistics |
+| `with_custom_attributes` | boolean | no | Include [custom attributes](custom_attributes.md) in response (admins only) |
| `with_issues_enabled` | boolean | no | Limit by enabled issues feature |
| `with_merge_requests_enabled` | boolean | no | Limit by enabled merge requests feature |
@@ -1220,7 +1224,7 @@ POST /projects/:id/hooks
| `note_events` | boolean | no | Trigger hook on note events |
| `job_events` | boolean | no | Trigger hook on job events |
| `pipeline_events` | boolean | no | Trigger hook on pipeline events |
-| `wiki_events` | boolean | no | Trigger hook on wiki events |
+| `wiki_page_events` | boolean | no | Trigger hook on wiki events |
| `enable_ssl_verification` | boolean | no | Do SSL verification when triggering the hook |
| `token` | string | no | Secret token to validate received payloads; this will not be returned in the response |
@@ -1326,6 +1330,10 @@ POST /projects/:id/housekeeping
Read more in the [Branches](branches.md) documentation.
+## Project Import/Export
+
+Read more in the [Project import/export](project_import_export.md) documentation.
+
## Project members
Read more in the [Project members](members.md) documentation.
diff --git a/doc/api/search.md b/doc/api/search.md
index 1fba9c3fbb8..d441b556186 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -737,7 +737,8 @@ Example response:
"filename": "home.md",
"id": null,
"ref": "master",
- "startline": 5
+ "startline": 5,
+ "project_id": 6
}
]
```
@@ -767,7 +768,8 @@ Example response:
"authored_date": "2013-02-18T22:02:54.000Z",
"committer_name": "angus croll",
"committer_email": "anguscroll@gmail.com",
- "committed_date": "2013-02-18T22:02:54.000Z"
+ "committed_date": "2013-02-18T22:02:54.000Z",
+ "project_id": 6
}
]
```
@@ -789,7 +791,8 @@ Example response:
"filename": "README.md",
"id": null,
"ref": "master",
- "startline": 46
+ "startline": 46,
+ "project_id": 6
}
]
```
diff --git a/doc/api/users.md b/doc/api/users.md
index 2082e45756a..a4447e32908 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -165,6 +165,12 @@ You can filter by [custom attributes](custom_attributes.md) with:
GET /users?custom_attributes[key]=value&custom_attributes[other_key]=other_value
```
+You can include the users' [custom attributes](custom_attributes.md) in the response with:
+
+```
+GET /users?with_custom_attributes=true
+```
+
## Single user
Get a single user.
@@ -245,6 +251,12 @@ Parameters:
}
```
+You can include the user's [custom attributes](custom_attributes.md) in the response with:
+
+```
+GET /users/:id?with_custom_attributes=true
+```
+
## User creation
Creates a new user. Note only administrators can create new users. Either `password` or `reset_password` should be specified (`reset_password` takes priority).
diff --git a/doc/ci/README.md b/doc/ci/README.md
index eabeb4510db..532ae52a184 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -70,6 +70,8 @@ learn how to leverage its potential even more.
- [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
## GitLab CI/CD for Docker
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 9f6b0c54990..ffebe1618d3 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -23,7 +23,7 @@ There's also a collection of repositories with [example projects](https://gitlab
- **Scala**: [Test a Scala application](test-scala-application.md)
- **Clojure**: [Test a Clojure application](test-clojure-application.md)
- **Elixir**:
- - [Test a Phoenix application](test-phoenix-application.md)
+ - [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/)
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 8e91cd05d8a..d931c9a77f4 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -1,14 +1,14 @@
---
redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
+author: Fabio Busatto
+author_gitlab: bikebilly
+level: intermediary
+article_type: tutorial
+date: 2017-08-15
---
# How to deploy Maven projects to Artifactory with GitLab CI/CD
-> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
-> **Level:** intermediary ||
-> **Author:** [Fabio Busatto](https://gitlab.com/bikebilly) ||
-> **Publication 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/)
diff --git a/doc/ci/examples/browser_performance.md b/doc/ci/examples/browser_performance.md
index 7bd0514d406..42dc6ef36ba 100644
--- a/doc/ci/examples/browser_performance.md
+++ b/doc/ci/examples/browser_performance.md
@@ -13,16 +13,20 @@ Once you set up the Runner, add a new job to `.gitlab-ci.yml`, called `performan
services:
- docker:dind
script:
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js
- mkdir sitespeed-results
- - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io --outputFolder sitespeed-results https://my.website.com
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results https://my.website.com
+ - mv sitespeed-results/data/performance.json performance.json
artifacts:
paths:
+ - performance.json
- sitespeed-results/
```
-This will create a `performance` job in your CI/CD pipeline and will run Sitespeed.io against the webpage you define. The full HTML Sitespeed.io report will be saved as an artifact, and if you have Pages enabled it can be viewed directly in your browser. For further customization options of Sitespeed.io, including the ability to provide a list of URLs to test, please consult their [documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/).
+This will create a `performance` job in your CI/CD pipeline and will run Sitespeed.io against the webpage you define. The GitLab plugin for Sitespeed.io is downloaded in order to export key metrics to JSON. The full HTML Sitespeed.io report will also be saved as an artifact, and if you have Pages enabled it can be viewed directly in your browser. For further customization options of Sitespeed.io, including the ability to provide a list of URLs to test, please consult their [documentation](https://www.sitespeed.io/documentation/sitespeed.io/configuration/).
-For [GitLab Premium](https://about.gitlab.com/products/) users, a performance score can be automatically
+For [GitLab Premium](https://about.gitlab.com/products/) users, key metrics are automatically
extracted and shown right in the merge request widget. Learn more about [Browser Performance Testing](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
## Performance testing on Review Apps
@@ -42,9 +46,15 @@ A simple `performance` job would look like:
- docker:dind
script:
- export CI_ENVIRONMENT_URL=$(cat environment_url.txt)
+ - mkdir gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/10-5/index.js
- mkdir sitespeed-results
- - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io --outputFolder sitespeed-results $CI_ENVIRONMENT_URL
+ - 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:
+ - performance.json
- sitespeed-results/
```
+
+A complete example can be found in our [Auto DevOps CI YML](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml). \ No newline at end of file
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 e1aff6fdf36..b62874ef029 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -1,14 +1,14 @@
---
redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
+author: Mehran Rasulian
+author_gitlab: mehranrasulian
+level: intermediary
+article_type: tutorial
+date: 2017-08-31
---
# Test and deploy Laravel applications with GitLab CI/CD and Envoy
-> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
-> **Level:** intermediary ||
-> **Author:** [Mehran Rasulian](https://gitlab.com/mehranrasulian) ||
-> **Publication date:** 2017-08-31
-
## Introduction
GitLab features our applications with Continuous Integration, and it is possible to easily deploy the new code changes to the production server whenever we want.
diff --git a/doc/ci/examples/test-phoenix-application.md b/doc/ci/examples/test-phoenix-application.md
index 7e49721daf1..52db5740c34 100644
--- a/doc/ci/examples/test-phoenix-application.md
+++ b/doc/ci/examples/test-phoenix-application.md
@@ -1,55 +1,5 @@
-# Test a Phoenix application with GitLab CI/CD
+---
+redirect_to: '../../ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md'
+---
-This example demonstrates the integration of Gitlab CI with Phoenix, Elixir and
-Postgres.
-
-## Add `.gitlab-ci.yml` to project
-
-The following `.gitlab-ci.yml` should be added in the root of your
-repository to trigger CI:
-
-```yaml
-image: elixir:1.3
-
-services:
- - postgres:9.6
-
-variables:
- MIX_ENV: "test"
-
-before_script:
- # Setup phoenix dependencies
- - apt-get update
- - apt-get install -y postgresql-client
- - mix local.hex --force
- - mix deps.get --only test
- - mix ecto.reset
-
-test:
- script:
- - mix test
-```
-
-The variables will set the Mix environment to "test". The
-`before_script` will install `psql`, some Phoenix dependencies, and will also
-run your migrations.
-
-Finally, the test `script` will run your tests.
-
-## Update the Config Settings
-
-In `config/test.exs`, update the database hostname:
-
-```elixir
-config :my_app, MyApp.Repo,
- hostname: if(System.get_env("CI"), do: "postgres", else: "localhost"),
-```
-
-## Add the Migrations Folder
-
-If you do not have any migrations yet, you will need to create an empty
-`.gitkeep` file in `priv/repo/migrations`.
-
-## Sources
-
-- https://medium.com/@nahtnam/using-phoenix-on-gitlab-ci-5a51eec81142
+The content of this page was incorporated in [this document](../../ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md).
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png
new file mode 100644
index 00000000000..0f94ac60fee
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/job-succeeded.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png
new file mode 100644
index 00000000000..94828a20f51
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-new.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png
new file mode 100644
index 00000000000..68503b392ed
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/mix-phoenix-server.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png
new file mode 100644
index 00000000000..d73140ccdd9
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/pipelines.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png
new file mode 100644
index 00000000000..38bfde0a3dd
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/select-template.png
Binary files differ
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png
new file mode 100644
index 00000000000..bfe85c6a10b
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/img/setup-ci.png
Binary files differ
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
new file mode 100644
index 00000000000..7f6519fd38e
--- /dev/null
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -0,0 +1,423 @@
+---
+author: Alexandre S Hostert
+author_gitlab: Hostert
+level: beginner
+article_type: tutorial
+date: 2018-02-20
+---
+
+# Testing a Phoenix application with GitLab CI/CD
+
+[Phoenix][phoenix-site] is a web development framework written in [Elixir][elixir-site], which is a
+functional language designed for productivity and maintainability that runs on the
+[Erlang VM][erlang-site]. Erlang VM is really really fast and can handle very large numbers of
+simultaneous users.
+
+That's why we're hearing so much about Phoenix today.
+
+In this tutorial, we'll teach you how to set up GitLab CI/CD to build and test a Phoenix
+application.
+
+_We assume that you know how to create a Phoenix app, run tests locally, and how to work with Git
+and GitLab UI._
+
+## Introduction
+
+### What is Phoenix?
+
+[Phoenix][phoenix-site] is a web development framework written in [Elixir][elixir-site] very useful
+ to build fast, reliable, and high-performance applications, as it uses [Erlang VM][erlang-site].
+
+Many components and concepts are similar to Ruby on Rails or Python's Django. High developer
+productivity and high application performance are only a few advantages on learning how to use it.
+Working on the MVC pattern, it's was designed to be modular and flexible. Easy to mantain a growing
+app is a plus.
+
+Phoenix can run in any OS where Erlang is supported:
+
+- Ubuntu
+- CentOS
+- Mac OS X
+- Debian
+- Windows
+- Fedora
+- Raspbian
+
+Check the [Phoenix learning guide][phoenix-learning-guide] for more information.
+
+### What is Elixir?
+
+[Elixir][elixir-site] is a dynamic, functional language created to use all the maturity of Erlang
+(30 years old!) in these days, in an easy way. It has similarities with Ruby, specially on sintax,
+so Ruby developers are quite excited with the rapid growing of Elixir. A full-stack Ruby developer
+can learn how to use Elixir and Phoenix in just a few weeks!
+
+In Elixir we have a command called `mix`, which is a helper to create projects, testing, run
+migrations and [much more][elixir-mix]. We'll use it later on in this tutorial.
+
+Check the [Elixir documentation][elixir-docs] for more information.
+
+## Requirements
+
+To follow this tutorial, you'll need to have installed:
+
+- Elixir [installation instructions][elixir-install]
+- Phoenix Framework [installation instructions][phoenix-install]
+- PostgreSQL (if you need to use MySQL server, check [Phoenix instructions][phoenix-mysql])
+
+### Create a new Phoenix project
+
+Open your terminal and go to the directory you wish to create your project.
+You don't need to create an empty directory for the project's files, because the `mix` command will
+do it for us.
+
+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:
+
+```bash
+mix phoenix.new hello_gitlab_ci
+```
+
+When asked, answer `Y` to fetch and install dependencies.
+
+If everything went fine, you'll get an output like this:
+
+![`mix phoenix.new`](img/mix-phoenix-new.png)
+
+Now, our project is located inside the directory with the same name we pass to `mix` command, for
+example, `~/GitLab/hello_gitlab_ci`.
+If we take a look at the directory, we'll see the Phoenix files and the dependencies needed to run.
+
+### Initialize the PostgreSQL database
+
+By default, Phoenix requires a PostgreSQL database to store whatever we need to store in our app. In
+this case, we'll only create an empty database.
+
+First, we need to navigate to our recently created project's directory, and then execute again
+`mix`. This time, `mix` will receive the parameter `ecto.create`, which is the task to create our
+new database. [Ecto][ecto] is the database wrapper for Elixir.
+
+When we do run `mix` the first time after creating our project, it will compile our files to
+bytecode, which will be interpreted by Erlang VM. In the next times, it will only compile our
+changes.
+
+Run the commands below to create our empty database:
+
+```bash
+cd hello_gitlab_ci
+mix ecto.create
+```
+
+We expect to see this output at the end of the command:
+
+```bash
+Generated hello_gitlab_ci app
+The database for HelloGitlabCi.Repo has been created
+```
+
+> **Note:**
+Phoenix assumes that our PostgreSQL database will have a `postgres` user account with the correct
+permissions and a password of `postgres`. If it's not your case, check
+[Ecto's instructions][ecto-repo].
+
+### Start Phoenix server
+
+Now, it's time to see if everything we did until now went well. We'll call `mix` again, this time
+with `phoenix.server` parameter, which will start Phoenix's HTTP Server.
+
+```bash
+mix phoenix.server
+```
+
+This will be the output to this command:
+
+```bash
+[info] Running HelloGitlabCi.Endpoint with Cowboy using http://localhost:4000
+23 May 11:44:35 - info: compiling
+23 May 11:44:37 - info: compiled 6 files into 2 files, copied 3 in 9.8 sec
+```
+
+Now, we have our app running locally. We can preview it directly on our browser. Let's open
+[`localhost:4000`](http://localhost:4000) to see our Phoenix Framework welcome page. If the link do
+not work, open [`127.0.0.1:4000`](http://127.0.0.1:4000) instead and later, configure your OS to
+point `localhost` to `127.0.0.1`.
+
+![`mix phoenix.server`](img/mix-phoenix-server.png)
+
+Great, now we have a local Phoenix Server running our app.
+
+Locally, our application is running in an `iex` session. [iex][iex] stands for Interactive Elixir.
+In this interactive mode, we can type any Elixir expression and get its result. To exit `iex`, we
+need to press `Ctrl+C` twice. So, when we need to stop the Phoenix server, we have to hit `Ctrl+C`
+twice.
+
+## Introducing GitLab CI/CD
+
+With GitLab, we can manage our development workflow, improve our productivity, track issues,
+perform code review, and much more from a single platform. With GitLab CI/CD, we can be much more
+productive, because every time we, or our co-workers push any code, GitLab CI/CD will build and
+test the changes, telling us in realtime if anything goes wrong.
+
+Certainly, when our application starts to grow, we'll need more developers working on the same
+project and this process of building and testing can easely become a mess without proper management.
+That's also why GitLab CI/CD is so important to our application. Every time someone pushes its code to
+GitLab, we'll quickly know if their changes broke something or not. We don't need to stop everything
+we're doing to test manually and locally every change our team does.
+
+Let's see this in practice.
+
+## Adjusting Phoenix configuration
+
+Now, we need to adjust our Phoenix configuration before configuring GitLab CI/CD.
+There is a directory (`config`) in your Phoenix project that contains a configuration file for every
+environment it can run. Since we will work with a single environment, we'll edit just the test
+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,
+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
+when running our Phoenix in our `localhost`.
+
+- Open `hello_gitlab_ci/config/test.exs` on your favorite code editor
+- Go to **Configure your database** session and edit the block to include `System.get_env`:
+
+ ```elixir
+ # Configure your database
+ config :hello_gitlab_ci, HelloGitlabCi.Repo,
+ adapter: Ecto.Adapters.Postgres,
+ username: System.get_env("POSTGRES_USER") || "postgres",
+ password: System.get_env("POSTGRES_PASSWORD") || "postgres",
+ database: System.get_env("POSTGRES_DB") || "hello_gitlab_ci_test",
+ hostname: System.get_env("POSTGRES_HOST") || "localhost",
+ pool: Ecto.Adapters.SQL.Sandbox
+ ```
+
+ We'll need these system variables later on.
+
+- Create an empty file named `.gitkeep` into `hello_gitlab_ci/priv/repo/migrations`
+
+ As our project is still fresh, we don't have any data on our database, so, the `migrations`
+directory will be empty.
+ Without `.gitkeep`, git will not upload this empty directory and we'll got an error when running our
+test on GitLab.
+
+ > **Note:**
+ If we add a folder via the GitLab UI, GitLab itself will add the `.gitkeep` to that new dir.
+
+Now, let's run a local test and see if everything we did didn't break anything.
+
+## Testing
+
+Earlier, when we created our project, we ran `mix phoenix.new`.
+This task created everything a Phoenix application needed, including some unit tests into
+`hello_gitlab_ci/test` directory.
+
+Let's run a new task with `mix` to run those tests for us. This time, the parameter expected is
+`test`. We can add `--trace` parameter for debugging purposes.
+
+In your terminal, navigate to the directory `hello_gitlab_ci` and run:
+
+```bash
+mix test
+```
+
+Our expected result is this:
+
+```bash
+....
+
+Finished in 0.7 seconds
+4 tests, 0 failures
+
+Randomized with seed 610000
+```
+
+Our test was successfull. It's time to push our files to GitLab.
+
+## Configuring CI/CD Pipeline
+
+The first step is to create a new file called `.gitlab-ci.yml` in `hello_gitlab_ci` directory of our
+project.
+
+- The fastest and easiest way to do this, is to click on **Set up CI** on project's main page:
+
+ ![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**:
+
+ ![Select template](img/select-template.png)
+
+ This template file tells GitLab CI/CD about what we wish to do every time a new commit is made.
+ However, we have to adapt it to run a Phoenix app.
+
+- The first line tells GitLab what Docker image will be used.
+
+ Remember when we learn about Runners, the isolated virtual machine where GitLab CI/CD build and test
+ our application? This virtual machine must have all dependencies to run our application. This is
+ where a Docker image is needed. The correct image will provide the entire system for us.
+
+ As a suggestion, you can use [trenpixster's elixir image][docker-image], which already has all
+ dependencies for Phoenix installed, such as Elixir, Erlang, NodeJS and PostgreSQL:
+
+ ```yml
+ image: trenpixster/elixir:latest
+ ```
+
+- At `services` session, we'll only use `postgres`, so we'll delete `mysql` and `redis` lines:
+
+ ```yml
+ services:
+ - postgres:latest
+ ```
+
+- Now, we'll create a new entry called `variables`, before `before_script` session:
+
+ ```yml
+ variables:
+ POSTGRES_DB: hello_gitlab_ci_test
+ POSTGRES_HOST: postgres
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: "postgres"
+ MIX_ENV: "test"
+ ```
+
+ Here, we are setting up the values for GitLab CI/CD authenticate into PostgreSQL, as we did on
+ `config/test.exs` earlier.
+
+- In `before_script` session, we'll add some commands to prepare everything to the test:
+
+ ```yml
+ before_script:
+ - apt-get update && apt-get -y install postgresql-client
+ - mix local.hex --force
+ - mix deps.get --only test
+ - mix ecto.create
+ - mix ecto.migrate
+ ```
+
+ It's important to install `postgresql-client` to let GitLab CI/CD access PostgreSQL and create our
+ database with the login information provided earlier. More important is to respect the identation,
+ to avoid syntax errors when running the build.
+
+- And finally, we'll let `mix` session intact.
+
+Let's take a look at the completed file after the editions:
+
+```yml
+image: trenpixster/elixir:latest
+
+services:
+ - postgres:latest
+
+variables:
+ POSTGRES_DB: test_test
+ POSTGRES_HOST: postgres
+ POSTGRES_USER: postgres
+ POSTGRES_PASSWORD: "postgres"
+ MIX_ENV: "test"
+
+before_script:
+ - apt-get update && apt-get -y install postgresql-client
+ - mix deps.get
+ - mix ecto.create
+ - mix ecto.migrate
+
+mix:
+ script:
+ - mix test
+```
+
+For safety, we can check if we get any syntax errors before submiting this file to GitLab. Copy the
+contents of `.gitlab-ci.yml` and paste it on [GitLab CI/CD Lint tool][ci-lint]. Please note that
+this link will only work for logged in users.
+
+## Watching the build
+
+I don't know about you, but I love to watch that black screen being filled with compilation output.
+With this, I can feel the happiness of something I made working correctly. On `localhost` it's easy
+to watch our build, but on GitLab, is it possible? Yes!
+
+Let's go to **Pipelines** and see GitLab doing the job. Just click on **Pipelines** to find the
+actual running build job.
+
+![Pipelines](img/pipelines.png)
+
+Click on build's ID to watch the entire process. If everything went as expected, we can wait for the
+**Build succeeded** at the end of the process! :)
+
+```
+$ mix test
+....
+
+Finished in 0.3 seconds
+4 tests, 0 failures
+
+Randomized with seed 206909
+Build succeeded
+```
+
+If we take a look at the project's main page on the GitLab UI, we can see the status of the last
+build made by GitLab CI/CD.
+
+Time to show the world our green build badge! Navigate to your project's **Settings > CI/CD** and
+expand **General pipelines settings**. Scroll down to **Pipeline status** and copy the markdown code
+for your badge. Paste it on the top of your `README.md` file, to let people outside of our project
+see if our latest code is running without errors.
+
+When we finish this edition, GitLab will start another build and show a **build running** badge. It
+is expected, after all we just configured GitLab CI/CD to do this for every push! But you may think
+"Why run build and tests for simple things like editing README.md?" and it is a good question.
+For changes that don't affect your application, you can add the keyword [`[ci skip]`][skipping-jobs]
+to commit message and the build related to that commit will be skipped.
+
+In the end, we finally got our pretty green build succeeded badge! By outputting the result on the
+README file, it shows to whoever lands on your project's page that your code is up-to-date and
+working properly.
+
+## Conclusion
+
+When we have a growing application with many developers working on it, or when we have an open
+source project being watched and contributed by the community, it is really important to have our
+code permanently working. GitLab CI/CD is a time saving powerfull tool to help us mantain our code
+organized and working.
+
+As we could see in this post, GitLab CI/CD is really really easy to configure and use. We have [many
+other reasons][ci-reasons] to keep using GitLab CI/CD. The benefits to our teams will be huge!
+
+## References
+
+- [GitLab CI/CD introductory guide][ci-guide]
+- [GitLab CI/CD full Documentation][ci-docs]
+- [GitLab Runners documentation][gitlab-runners]
+- [Using Docker images documentation][using-docker]
+- [Example project: Hello GitLab CI/CD on GitLab][hello-gitlab]
+
+[phoenix-site]: http://phoenixframework.org/ "Phoenix Framework"
+[phoenix-learning-guide]: https://hexdocs.pm/phoenix/learning.html "Phoenix Learning Guide"
+[phoenix-install]: http://www.phoenixframework.org/docs/installation "Phoenix Installation"
+[phoenix-mysql]: http://www.phoenixframework.org/docs/using-mysql "Phoenix with MySQL"
+[elixir-site]: http://elixir-lang.org/ "Elixir"
+[elixir-mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html "Introduction to mix"
+[elixir-docs]: http://elixir-lang.org/getting-started/introduction.html "Elixir Documentation"
+[erlang-site]: http://erlang.org "Erlang"
+[elixir-install]: https://elixir-lang.org/install.html "Elixir Installation"
+[ecto]: http://hexdocs.pm/ecto "Ecto"
+[ecto-repo]: https://hexdocs.pm/ecto/Ecto.html#module-repositories "Ecto Repositories"
+[mix-ecto]: https://hexdocs.pm/ecto/Mix.Tasks.Ecto.Create.html "mix and Ecto"
+[iex]: http://elixir-lang.org/getting-started/introduction.html#interactive-mode "Interactive Mode"
+[ci-lint]: https://gitlab.com/ci/lint "CI Lint Tool"
+[ci-reasons]: https://about.gitlab.com/2015/02/03/7-reasons-why-you-should-be-using-ci/ "7 Reasons Why You Should Be Using CI"
+[ci-guide]: https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/ "Getting Started With GitLab And GitLab CI/CD"
+[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/yaml/README.md b/doc/ci/yaml/README.md
index 4a650303d45..80ab63468f2 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -276,7 +276,7 @@ To enable per-branch caching:
```yaml
cache:
- key: "$CI_COMMIT_REF_NAME"
+ key: "$CI_COMMIT_REF_SLUG"
untracked: true
```
@@ -284,7 +284,7 @@ To enable per-job and per-branch caching:
```yaml
cache:
- key: "$CI_JOB_NAME-$CI_COMMIT_REF_NAME"
+ key: "$CI_JOB_NAME-$CI_COMMIT_REF_SLUG"
untracked: true
```
@@ -292,7 +292,7 @@ To enable per-branch and per-stage caching:
```yaml
cache:
- key: "$CI_JOB_STAGE-$CI_COMMIT_REF_NAME"
+ key: "$CI_JOB_STAGE-$CI_COMMIT_REF_SLUG"
untracked: true
```
@@ -301,7 +301,7 @@ If you use **Windows Batch** to run your shell scripts you need to replace
```yaml
cache:
- key: "%CI_JOB_STAGE%-%CI_COMMIT_REF_NAME%"
+ key: "%CI_JOB_STAGE%-%CI_COMMIT_REF_SLUG%"
untracked: true
```
@@ -310,7 +310,7 @@ If you use **Windows PowerShell** to run your shell scripts you need to replace
```yaml
cache:
- key: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_NAME"
+ key: "$env:CI_JOB_STAGE-$env:CI_COMMIT_REF_SLUG"
untracked: true
```
diff --git a/doc/customization/branded_login_page.md b/doc/customization/branded_login_page.md
index d4d9f5f7b5e..b892f59d777 100644
--- a/doc/customization/branded_login_page.md
+++ b/doc/customization/branded_login_page.md
@@ -1,6 +1,6 @@
# Changing the appearance of the login page
-GitLab Community Edition offers a way to put your company's identity on the login page of your GitLab server and make it a branded login page.
+GitLab offers a way to put your company's identity on the login page of your GitLab server and make it a branded login page.
By default, the page shows the GitLab logo and description.
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index d1ba7d3dfc3..31117b5e723 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -2,9 +2,11 @@
## Software delivery
-There are two editions of GitLab: [Enterprise Edition](https://about.gitlab.com/gitlab-ee/) (EE) and [Community Edition](https://about.gitlab.com/gitlab-ce/) (CE). GitLab CE is delivered via git from the [gitlabhq repository](https://gitlab.com/gitlab-org/gitlab-ce/tree/master). New versions of GitLab are released in stable branches and the master branch is for bleeding edge development.
+There are two software distributions of GitLab: the open source [Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE), and the open core [Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE). GitLab is available under [different subscriptions](https://about.gitlab.com/products/).
-EE releases are available not long after CE releases. To obtain the GitLab EE there is a [repository at gitlab.com](https://gitlab.com/gitlab-org/gitlab-ee). For more information about the release process see the section 'New versions and upgrading' in the readme.
+New versions of GitLab are released in stable branches and the master branch is for bleeding edge development.
+
+For information, see the [GitLab Release Process](https://gitlab.com/gitlab-org/release/docs/tree/master#gitlab-release-process).
Both EE and CE require some add-on components called gitlab-shell and Gitaly. These components are available from the [gitlab-shell](https://gitlab.com/gitlab-org/gitlab-shell/tree/master) and [gitaly](https://gitlab.com/gitlab-org/gitaly/tree/master) repositories respectively. New versions are usually tags but staying on the master branch will give you the latest stable version. New releases are generally around the same time as GitLab CE releases with exception for informal security updates deemed critical.
diff --git a/doc/development/background_migrations.md b/doc/development/background_migrations.md
index af2026c483e..fc1b202b5eb 100644
--- a/doc/development/background_migrations.md
+++ b/doc/development/background_migrations.md
@@ -94,6 +94,18 @@ jobs = [['BackgroundMigrationClassName', [1]],
BackgroundMigrationWorker.bulk_perform_in(5.minutes, jobs)
```
+### Rescheduling background migrations
+
+If one of the background migrations contains a bug that is fixed in a patch
+release, the background migration needs to be rescheduled so the migration would
+be repeated on systems that already performed the initial migration.
+
+When you reschedule the background migration, make sure to turn the original
+scheduling into a no-op by clearing up the `#up` and `#down` methods of the
+migration performing the scheduling. Otherwise the background migration would be
+scheduled multiple times on systems that are upgrading multiple patch releases at
+once.
+
## Cleaning Up
>**Note:**
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index c1f783ce877..1962392a9eb 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -125,7 +125,7 @@ author:
type:
```
If you're working on the GitLab EE repository, the entry will be added to
-`changelogs/unreleased-ee/` instead.
+`ee/changelogs/unreleased/` instead.
### Arguments
@@ -279,8 +279,8 @@ After much discussion we settled on the current solution of one file per entry,
and then compiling the entries into the overall `CHANGELOG.md` file during the
[release process].
-[boring solution]: https://about.gitlab.com/handbook/#boring-solutions
-[release managers]: https://gitlab.com/gitlab-org/release-tools/blob/master/doc/release-manager.md
+[boring solution]: https://about.gitlab.com/handbook/values/#boring-solutions
+[release managers]: https://gitlab.com/gitlab-org/release/docs/blob/master/quickstart/release-manager.md
[started brainstorming]: https://gitlab.com/gitlab-org/gitlab-ce/issues/17826
[release process]: https://gitlab.com/gitlab-org/release-tools
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
index cfeeed2506d..6fe5f647d6c 100644
--- a/doc/development/doc_styleguide.md
+++ b/doc/development/doc_styleguide.md
@@ -236,6 +236,11 @@ Inside the document:
## New features
+New features must be shipped with its accompanying documentation and the doc
+reviewed by a technical writer.
+
+### Mentioning GitLab versions and tiers
+
- Every piece of documentation that comes with a new feature should declare the
GitLab version that feature got introduced. Right below the heading add a
note:
@@ -244,7 +249,7 @@ Inside the document:
> Introduced in GitLab 8.3.
```
-- If possible every feature should have a link to the MR that introduced it.
+- If possible every feature should have a link to the MR, issue, or epic that introduced it.
The above note would be then transformed to:
```
@@ -254,11 +259,12 @@ Inside the document:
, where the [link identifier](#links) is named after the repository (CE) and
the MR number.
-- If the feature is only in GitLab Enterprise Edition, don't forget to mention
- it, like:
+- If the feature is only available in GitLab Enterprise Edition, don't forget to mention
+ the [paid tier](https://about.gitlab.com/handbook/marketing/product-marketing/#tiers)
+ the feature is available in:
```
- > Introduced in GitLab Enterprise Edition 8.3.
+ > [Introduced][ee-1234] in [GitLab Starter](https://about.gitlab.com/products/) 8.3.
```
Otherwise, leave this mention out.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index f8cee89e650..f6a14de96b2 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -28,9 +28,8 @@ we still need to merge changes from GitLab CE to EE. To help us get there,
we should make sure that we no longer edit CE files in place in order to
implement EE features.
-Instead, all EE codes should be put inside the `ee/` top-level directory, and
-tests should be put inside `spec/ee/`. We don't use `ee/spec` for now due to
-technical limitation. The rest of codes should be as close as to the CE files.
+Instead, all EE code should be put inside the `ee/` top-level directory. The
+rest of the code should be as close to the CE files as possible.
[single code base]: https://gitlab.com/gitlab-org/gitlab-ee/issues/2952#note_41016454
@@ -318,7 +317,7 @@ When you're testing EE-only features, avoid adding examples to the
existing CE specs. Also do no change existing CE examples, since they
should remain working as-is when EE is running without a license.
-Instead place EE specs in the `spec/ee/spec` folder.
+Instead place EE specs in the `ee/spec` folder.
## JavaScript code in `assets/javascripts/`
diff --git a/doc/development/fe_guide/components.md b/doc/development/fe_guide/components.md
new file mode 100644
index 00000000000..66a8abe42f7
--- /dev/null
+++ b/doc/development/fe_guide/components.md
@@ -0,0 +1,61 @@
+# Components
+
+## Contents
+* [Dropdowns](#dropdowns)
+* [Modals](#modals)
+
+## Dropdowns
+
+See also the [corresponding UX guide](../ux_guide/components.md#dropdowns).
+
+### How to style a bootstrap dropdown
+1. Use the HTML structure provided by the [docs][bootstrap-dropdowns]
+1. Add a specific class to the top level `.dropdown` element
+
+
+ ```Haml
+ .dropdown.my-dropdown
+ %button{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false }
+ %span.dropdown-toggle-text
+ Toggle Dropdown
+ = icon('chevron-down')
+
+ %ul.dropdown-menu
+ %li
+ %a
+ item!
+ ```
+
+ Or use the helpers
+ ```Haml
+ .dropdown.my-dropdown
+ = dropdown_toggle('Toogle!', { toggle: 'dropdown' })
+ = dropdown_content
+ %li
+ %a
+ item!
+ ```
+
+[bootstrap-dropdowns]: https://getbootstrap.com/docs/3.3/javascript/#dropdowns
+
+## Modals
+
+See also the [corresponding UX guide](../ux_guide/components.md#modals).
+
+We have a reusable Vue component for modals: [vue_shared/components/gl-modal.vue](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/vue_shared/components/gl-modal.vue)
+
+Here is an example of how to use it:
+
+```html
+ <gl-modal
+ id="dogs-out-modal"
+ :header-title-text="s__('ModalExample|Let the dogs out?')"
+ footer-primary-button-variant="danger"
+ :footer-primary-button-text="s__('ModalExample|Let them out')"
+ @submit="letOut(theDogs)"
+ >
+ {{ s__('ModalExample|You’re about to let the dogs out.') }}
+ </gl-modal>
+```
+
+![example modal](img/gl-modal.png)
diff --git a/doc/development/fe_guide/dropdowns.md b/doc/development/fe_guide/dropdowns.md
index 6314f8f38d2..e9d6244355c 100644
--- a/doc/development/fe_guide/dropdowns.md
+++ b/doc/development/fe_guide/dropdowns.md
@@ -1,32 +1 @@
-# Dropdowns
-
-
-## How to style a bootstrap dropdown
-1. Use the HTML structure provided by the [docs][bootstrap-dropdowns]
-1. Add a specific class to the top level `.dropdown` element
-
-
- ```Haml
- .dropdown.my-dropdown
- %button{ type: 'button', data: { toggle: 'dropdown' }, 'aria-haspopup': true, 'aria-expanded': false }
- %span.dropdown-toggle-text
- Toggle Dropdown
- = icon('chevron-down')
-
- %ul.dropdown-menu
- %li
- %a
- item!
- ```
-
- Or use the helpers
- ```Haml
- .dropdown.my-dropdown
- = dropdown_toggle('Toogle!', { toggle: 'dropdown' })
- = dropdown_content
- %li
- %a
- item!
- ```
-
-[bootstrap-dropdowns]: https://getbootstrap.com/docs/3.3/javascript/#dropdowns
+This page has moved [here](components.md#dropdowns).
diff --git a/doc/development/fe_guide/img/gl-modal.png b/doc/development/fe_guide/img/gl-modal.png
new file mode 100644
index 00000000000..47302e857bc
--- /dev/null
+++ b/doc/development/fe_guide/img/gl-modal.png
Binary files differ
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index 72cb557d054..12dfc10812b 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -21,6 +21,8 @@ Working with our frontend assets requires Node (v4.3 or greater) and Yarn
[jQuery][jquery] is used throughout the application's JavaScript, with
[Vue.js][vue] for particularly advanced, dynamic elements.
+We also use [Axios][axios] to handle all of our network requests.
+
### Browser Support
For our currently-supported browsers, see our [requirements][requirements].
@@ -77,8 +79,10 @@ Axios specific practices and gotchas.
## [Icons](icons.md)
How we use SVG for our Icons.
-## [Dropdowns](dropdowns.md)
-How we use dropdowns.
+## [Components](components.md)
+
+How we use UI components.
+
---
## Style Guides
@@ -122,6 +126,7 @@ The [externalization part of the guide](../i18n/externalization.md) explains the
[webpack]: https://webpack.js.org/
[jquery]: https://jquery.com/
[vue]: http://vuejs.org/
+[axios]: https://github.com/axios/axios
[airbnb-js-style-guide]: https://github.com/airbnb/javascript
[scss-lint]: https://github.com/brigade/scss-lint
[install]: ../../install/installation.md#4-node
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index 02773162801..917d28b48ee 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -207,10 +207,39 @@ Do not use them anymore and feel free to remove them when refactoring legacy cod
var c = pureFunction(values.foo);
```
-1. Avoid constructors with side-effects
+1. Avoid constructors with side-effects.
+Although we aim for code without side-effects we need some side-effects for our code to run.
+
+If the class won't do anything if we only instantiate it, it's ok to add side effects into the constructor (_Note:_ The following is just an example. If the only purpose of the class is to add an event listener and handle the callback a function will be more suitable.)
+
+```javascript
+// Bad
+export class Foo {
+ constructor() {
+ this.init();
+ }
+ init() {
+ document.addEventListener('click', this.handleCallback)
+ },
+ handleCallback() {
+
+ }
+}
+
+// Good
+export class Foo {
+ constructor() {
+ document.addEventListener()
+ }
+ handleCallback() {
+ }
+}
+```
+
+On the other hand, if a class only needs to extend a third party/add event listeners in some specific cases, they should be initialized oustside of the constructor.
1. Prefer `.map`, `.reduce` or `.filter` over `.forEach`
-A forEach will cause side effects, it will be mutating the array being iterated. Prefer using `.map`,
+A forEach will most likely cause side effects, it will be mutating the array being iterated. Prefer using `.map`,
`.reduce` or `.filter`
```javascript
const users = [ { name: 'Foo' }, { name: 'Bar' } ];
@@ -302,20 +331,20 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
#### Naming
1. **Extensions**: Use `.vue` extension for Vue components.
-1. **Reference Naming**: Use camelCase for their instances:
+1. **Reference Naming**: Use PascalCase for their instances:
```javascript
// bad
- import CardBoard from 'cardBoard'
+ import cardBoard from 'cardBoard.vue'
components: {
- CardBoard:
+ cardBoard,
};
// good
- import cardBoard from 'cardBoard'
+ import CardBoard from 'cardBoard.vue'
components: {
- cardBoard:
+ CardBoard,
};
```
diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md
index 86a8b4135af..655d94793dd 100644
--- a/doc/development/fe_guide/style_guide_scss.md
+++ b/doc/development/fe_guide/style_guide_scss.md
@@ -7,6 +7,8 @@ easy to maintain, and performant for the end-user.
### Naming
+Filenames should use `snake_case`.
+
CSS classes should use the `lowercase-hyphenated` format rather than
`snake_case` or `camelCase`.
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index 59e8a087e02..5d1f657015c 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -1,6 +1,6 @@
# Manage feature flags
-Starting from GitLab 9.3 we support feature flags via
+Starting from GitLab 9.3 we support feature flags for features in GitLab via
[Flipper](https://github.com/jnunemaker/flipper/). You should use the `Feature`
class (defined in `lib/feature.rb`) in your code to get, set and list feature
flags.
@@ -19,3 +19,8 @@ dynamic (querying the DB etc.).
Once defined in `lib/feature.rb`, you will be able to activate a
feature for a given feature group via the [`feature_group` param of the features API](../api/features.md#set-or-create-a-feature)
+
+## 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). \ No newline at end of file
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index f4542932295..c0a325a83e9 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -126,6 +126,9 @@ strings and remove any strings that aren't used anymore. You should check this
file in. Once the changes are on master, they will be picked up by
[Crowdin](http://translate.gitlab.com) and be presented for translation.
+If there are merge conflicts in the `gitlab.pot` file, you can delete the file
+and regenerate it using the same command. Confirm that you are not deleting any strings accidentally by looking over the diff.
+
The command also updates the translation files for each language: `locale/*/gitlab.po`
These changes can be discarded, the languange files will be updated by Crowdin
automatically.
diff --git a/doc/development/i18n/index.md b/doc/development/i18n/index.md
index 8aa0462d213..7290a175501 100644
--- a/doc/development/i18n/index.md
+++ b/doc/development/i18n/index.md
@@ -40,37 +40,12 @@ See [Translation guidelines](translation.md).
### Proof reading
-Proof reading helps ensure the accuracy and consistency of translations.
-All translations are proof read before being accepted.
-If a translations requires changes, you will be notified with a comment explaining why.
-
-Community assistance proof reading translations is encouraged and appreciated.
-Requests to become a proof reader will be considered on the merits of previous translations.
-
-- Bulgarian
-- Chinese Simplified
- - [Huang Tao](https://crowdin.com/profile/htve)
-- Chinese Traditional
- - [Huang Tao](https://crowdin.com/profile/htve)
-- Chinese Traditional, Hong Kong
- - [Huang Tao](https://crowdin.com/profile/htve)
-- Dutch
-- Esperanto
-- French
-- German
-- Italian
- - [Paolo Falomo](https://crowdin.com/profile/paolo.falomo)
-- Japanese
-- Korean
- - [Huang Tao](https://crowdin.com/profile/htve)
-- Portuguese, Brazilian
-- Russian
- - [Alexy Lustin](https://crowdin.com/profile/lustin)
- - [Nikita Grylov](https://crowdin.com/profile/nixel2007)
-- Spanish
-- Ukrainian
-
-If you would like to be added as a proof reader, please [open an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues).
+Proof reading helps ensure the accuracy and consistency of translations. All
+translations are proof read before being accepted. If a translations requires
+changes, you will be notified with a comment explaining why.
+
+See [Proofreading Translations](proofreader.md) for more information on who's
+able to proofread and instructions on becoming a proofreader yourself.
## Release
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
new file mode 100644
index 00000000000..ece9a9bc0fe
--- /dev/null
+++ b/doc/development/i18n/proofreader.md
@@ -0,0 +1,67 @@
+# Proofread Translations
+
+Most translations are contributed, reviewed, and accepted by the community. We
+are very appreciative of the work done by translators and proofreaders!
+
+## Proofreaders
+
+- Bulgarian
+- Chinese Simplified
+ - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+- Chinese Traditional
+ - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+- Chinese Traditional, Hong Kong
+ - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+- Dutch
+- Esperanto
+- French
+- German
+- Italian
+ - Paolo Falomo - [GitLab](https://gitlab.com/paolofalomo), [Crowdin](https://crowdin.com/profile/paolo.falomo)
+- Japanese
+- Korean
+ - Huang Tao - [GitLab](https://gitlab.com/htve), [Crowdin](https://crowdin.com/profile/htve)
+- Portuguese, Brazilian
+ - Paulo George Gomes Bezerra - [GitLab](https://gitlab.com/paulobezerra), [Crowdin](https://crowdin.com/profile/paulogomes.rep)
+- Russian
+ - Nikita Grylov - [GitLab](https://gitlab.com/nixel2007), [Crowdin](https://crowdin.com/profile/nixel2007)
+ - Alexy Lustin - [GitLab](https://gitlab.com/allustin), [Crowdin](https://crowdin.com/profile/lustin)
+- Spanish
+- Ukrainian
+ - Volodymyr Sobotovych - [GitLab](https://gitlab.com/wheleph), [Crowdin](https://crowdin.com/profile/wheleph)
+ - Andrew Vityuk - [GitLab](https://gitlab.com/3_1_3_u), [Crowdin](https://crowdin.com/profile/andruwa13)
+
+## Become a proofreader
+
+> **Note:** Before requesting Proofreader permissions in Crowdin please make
+> sure that you have a history of contributing translations to the GitLab
+> project.
+
+1. Contribute translations to GitLab. See instructions for
+ [translating GitLab](translation.md).
+
+ Translating GitLab is a community effort that requires team work and
+ attention to detail. Proofreaders play an important role helping new
+ contributors, and ensuring the consistency and quality of translations.
+ Your conduct and contributions as a translator should reflect this before
+ requesting to be a proofreader.
+
+1. Request proofreader permissions by opening a merge request to add yourself
+ to the list of proofreaders.
+
+ Open the [proofreader.md source file][proofreader-src] and click **Edit**.
+
+ Add your language in alphabetical order, and add yourself to the list
+ including:
+
+ - name
+ - link to your GitLab profile
+ - link to your CrowdIn profile
+
+ In the merge request description, please include links to any projects you
+ have previously translated.
+
+1. Your request to become a proofreader will be considered on the merits of
+ your previous translations.
+
+[proofreader-src]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/i18n/proofreader.md
diff --git a/doc/development/licensing.md b/doc/development/licensing.md
index 274923c2d43..c06bc0d4731 100644
--- a/doc/development/licensing.md
+++ b/doc/development/licensing.md
@@ -1,6 +1,6 @@
# GitLab Licensing and Compatibility
-GitLab CE is licensed under the terms of the MIT License. GitLab EE is licensed under "The GitLab Enterprise Edition (EE) license" wherein there are more restrictions. See their respective LICENSE files ([CE][CE], [EE][EE]) for more information.
+[GitLab Community Edition](https://gitlab.com/gitlab-org/gitlab-ce/) (CE) is licensed [under the terms of the MIT License][CE]. [GitLab Enterprise Edition](https://gitlab.com/gitlab-org/gitlab-ee/) (EE) is licensed under "[The GitLab Enterprise Edition (EE) license][EE]" wherein there are more restrictions.
## Automated Testing
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index 97c997e0568..11878b4009b 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -27,6 +27,17 @@ Gitlab::Profiler.profile('/my-user')
# Returns a RubyProf::Profile where 100 seconds is spent in UsersController#show
```
+For routes that require authorization you will need to provide a user to
+`Gitlab::Profiler`. You can do this like so:
+
+```ruby
+Gitlab::Profiler.profile('/gitlab-org/gitlab-test', user: User.first)
+```
+
+The user you provide will need to have a [personal access
+token](https://docs.gitlab.com/ce/user/profile/personal_access_tokens.html) in
+the GitLab instance.
+
Passing a `logger:` keyword argument to `Gitlab::Profiler.profile` will send
ActiveRecord and ActionController log output to that logger. Further options are
documented with the method source.
diff --git a/doc/development/query_count_limits.md b/doc/development/query_count_limits.md
index ebb6e0c2dac..310e3faf61b 100644
--- a/doc/development/query_count_limits.md
+++ b/doc/development/query_count_limits.md
@@ -1,8 +1,7 @@
# Query Count Limits
-Each controller or API endpoint is allowed to execute up to 100 SQL queries. In
-a production environment we'll only log an error in case this threshold is
-exceeded, but in a test environment we'll raise an error instead.
+Each controller or API endpoint is allowed to execute up to 100 SQL queries and
+in test environments we'll raise an error when this threshold is exceeded.
## Solving Failing Tests
diff --git a/doc/development/sidekiq_style_guide.md b/doc/development/sidekiq_style_guide.md
index 59ebf41e09f..76ff51446ba 100644
--- a/doc/development/sidekiq_style_guide.md
+++ b/doc/development/sidekiq_style_guide.md
@@ -17,6 +17,9 @@ would be `process_something`. If you're not sure what queue a worker uses,
you can find it using `SomeWorker.queue`. There is almost never a reason to
manually override the queue name using `sidekiq_options queue: :some_queue`.
+You must always add any new queues to `app/workers/all_queues.yml` otherwise
+your worker will not run.
+
## Queue Namespaces
While different workers cannot share a queue, they can share a queue namespace.
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index 4adf0dc7c7a..e86c1f5232a 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -134,6 +134,10 @@ learn more.
[GitLab QA]: https://gitlab.com/gitlab-org/gitlab-qa
[part of GitLab Rails]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/qa
+## EE-specific tests
+
+EE-specific tests follows the same organization, but under the `ee/spec` folder.
+
## How to test at the correct level?
As many things in life, deciding what to test at each level of testing is a
diff --git a/doc/development/writing_documentation.md b/doc/development/writing_documentation.md
index 2a1d744668b..403c9d08752 100644
--- a/doc/development/writing_documentation.md
+++ b/doc/development/writing_documentation.md
@@ -240,7 +240,7 @@ Suppose there's a process to go from point A to point B in 5 steps: `(A) 1 > 2 >
A **guide** can be understood as a description of certain processes to achieve a particular objective. A guide brings you from A to B describing the characteristics of that process, but not necessarily going over each step. It can mention, for example, steps 2 and 3, but does not necessarily explain how to accomplish them.
-- Live example: "GitLab Pages from A to Z - [Part 1](../user/project/pages/getting_started_part_one.md) to [Part 4](../user/project/pages/getting_started_part_four.md)"
+- Live example: "[Static sites and GitLab Pages domains (Part 1)](../user/project/pages/getting_started_part_one.md) to [Creating and Tweaking GitLab CI/CD for GitLab Pages (Part 4)](../user/project/pages/getting_started_part_four.md)"
A **tutorial** requires a clear **step-by-step** guidance to achieve a singular objective. It brings you from A to B, describing precisely all the necessary steps involved in that process, showing each of the 5 steps to go from A to B.
It does not only describes steps 2 and 3, but also shows you how to accomplish them.
@@ -254,18 +254,25 @@ through the process of how to use it systematically.
#### Special format
-Every **Technical Article** contains, in the very beginning, a blockquote with the following information:
+Every **Technical Article** contains a frontmatter at the beginning of the doc
+with the following information:
-- A reference to the **type of article** (user guide, admin guide, tech overview, tutorial)
-- A reference to the **knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
-- A reference to the **author's name** and **GitLab.com handle**
-- A reference of the **publication date**
+- **Type of article** (user guide, admin guide, technical overview, tutorial)
+- **Knowledge level** expected from the reader to be able to follow through (beginner, intermediate, advanced)
+- **Author's name** and **GitLab.com handle**
+- **Publication date** (ISO format YYYY-MM-DD)
-```md
-> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
-> **Level:** intermediary ||
-> **Author:** [Name Surname](https://gitlab.com/username) ||
-> **Publication date:** AAAA-MM-DD
+For example:
+
+
+```yaml
+---
+author: John Doe
+author_gitlab: johnDoe
+level: beginner
+article_type: user guide
+date: 2017-02-01
+---
```
#### Technical Articles - Writing Method
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 7b87039da84..10e8059756d 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -41,16 +41,16 @@ When you create a new repo locally, instead of going to GitLab to manually
create a new project and then push the repo, you can directly push it to
GitLab to create the new project, all without leaving your terminal. If you have access to that
namespace, we will automatically create a new project under that GitLab namespace with its
-visibility set to private by default (you can later change it in the UI).
+visibility set to Private by default (you can later change it in the [project's settings](../public_access/public_access.md#how-to-change-project-visibility)).
This can be done by using either SSH or HTTP:
```
## Git push using SSH
-git push git@gitlab.example.com:namespace/nonexistent-project.git
+git push --set-upstream git@gitlab.example.com:namespace/nonexistent-project.git master
## Git push using HTTP
-git push https://gitlab.example.com/namespace/nonexistent-project.git
+git push --set-upstream https://gitlab.example.com/namespace/nonexistent-project.git master
```
Once the push finishes successfully, a remote message will indicate
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index 7afe338ae8b..b0c3ad960bb 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -38,9 +38,10 @@ create SQL Databases, author websites, and perform lots of other cloud tasks.
## Create New VM
The [Azure Marketplace][Azure-Marketplace] is an online store for pre-configured applications and
-services which have been optimized for the cloud by software vendors like GitLab, and both
-the [Community Edition ("CE")][CE] and the [Enterprise Edition ("EE")][EE] versions of GitLab are
-available on the Azure Marketplace as pre-configured solutions.
+services which have been optimized for the cloud by software vendors like GitLab,
+available on the Azure Marketplace as pre-configured solutions. In this tutorial
+we will install GitLab Community Edition, but for GitLab Enterprise Edition you
+can follow the same process.
To begin creating a new GitLab VM, click on the **+ New** icon, type "GitLab" into the search
box, and then click the **"GitLab Community Edition"** search result:
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index f9ba1508705..5c7557ed2b3 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -6,7 +6,6 @@
and [problems](https://bugs.mysql.com/bug.php?id=65830) that
[suggested](https://bugs.mysql.com/bug.php?id=50909)
[fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164).
-- We recommend using MySQL version 5.6 or later. Please see the following [issue][ce-38152].
## Initial database setup
diff --git a/doc/install/google_cloud_platform/img/boot_disk.png b/doc/install/google_cloud_platform/img/boot_disk.png
new file mode 100644
index 00000000000..37b2d9eaae7
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/boot_disk.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png b/doc/install/google_cloud_platform/img/change_admin_passwd_email.png
deleted file mode 100644
index 1ffe14f60ff..00000000000
--- a/doc/install/google_cloud_platform/img/change_admin_passwd_email.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png b/doc/install/google_cloud_platform/img/chrome_not_secure_page.png
deleted file mode 100644
index e732066908f..00000000000
--- a/doc/install/google_cloud_platform/img/chrome_not_secure_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/first_signin.png b/doc/install/google_cloud_platform/img/first_signin.png
new file mode 100644
index 00000000000..6eb3392d674
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/first_signin.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png b/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png
deleted file mode 100644
index 2a1859da6e3..00000000000
--- a/doc/install/google_cloud_platform/img/gcp_gitlab_being_deployed.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png b/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png
deleted file mode 100644
index 1c4c870dbc9..00000000000
--- a/doc/install/google_cloud_platform/img/gcp_gitlab_overview.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_landing.png b/doc/install/google_cloud_platform/img/gcp_landing.png
index 6398d247ba0..d6390c4dd4f 100644
--- a/doc/install/google_cloud_platform/img/gcp_landing.png
+++ b/doc/install/google_cloud_platform/img/gcp_landing.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png b/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png
deleted file mode 100644
index f492888ea4a..00000000000
--- a/doc/install/google_cloud_platform/img/gcp_launcher_console_home_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png b/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png
deleted file mode 100644
index b38af3966e2..00000000000
--- a/doc/install/google_cloud_platform/img/gcp_search_for_gitlab.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png b/doc/install/google_cloud_platform/img/gitlab_deployed_page.png
deleted file mode 100644
index fef9ae45f32..00000000000
--- a/doc/install/google_cloud_platform/img/gitlab_deployed_page.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png b/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png
deleted file mode 100644
index 381c0fe48a5..00000000000
--- a/doc/install/google_cloud_platform/img/gitlab_first_sign_in.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/gitlab_launch_button.png b/doc/install/google_cloud_platform/img/gitlab_launch_button.png
deleted file mode 100644
index 50f66f66118..00000000000
--- a/doc/install/google_cloud_platform/img/gitlab_launch_button.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/launch_vm.png b/doc/install/google_cloud_platform/img/launch_vm.png
new file mode 100644
index 00000000000..3fd13f232bb
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/launch_vm.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png b/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png
deleted file mode 100644
index 00060841619..00000000000
--- a/doc/install/google_cloud_platform/img/new_gitlab_deployment_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/ssh_terminal.png b/doc/install/google_cloud_platform/img/ssh_terminal.png
new file mode 100644
index 00000000000..6a1a418d8e9
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/ssh_terminal.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/ssh_via_button.png b/doc/install/google_cloud_platform/img/ssh_via_button.png
deleted file mode 100644
index 26106f159ad..00000000000
--- a/doc/install/google_cloud_platform/img/ssh_via_button.png
+++ /dev/null
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/vm_created.png b/doc/install/google_cloud_platform/img/vm_created.png
new file mode 100644
index 00000000000..fb467f40838
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/vm_created.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/img/vm_details.png b/doc/install/google_cloud_platform/img/vm_details.png
new file mode 100644
index 00000000000..2d230416a4b
--- /dev/null
+++ b/doc/install/google_cloud_platform/img/vm_details.png
Binary files differ
diff --git a/doc/install/google_cloud_platform/index.md b/doc/install/google_cloud_platform/index.md
index c6b767fff02..3389f0260f9 100644
--- a/doc/install/google_cloud_platform/index.md
+++ b/doc/install/google_cloud_platform/index.md
@@ -2,12 +2,7 @@
![GCP landing page](img/gcp_landing.png)
-The fastest way to get started on [Google Cloud Platform (GCP)][gcp] is through
-the [Google Cloud Launcher][launcher] program.
-
-GitLab's official Google Launcher apps:
-1. [GitLab Community Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-community-edition?project=gitlab-public)
-2. [GitLab Enterprise Edition](https://console.cloud.google.com/launcher/details/gitlab-public/gitlab-enterprise-edition?project=gitlab-public)
+Gettung started with GitLab on a [Google Cloud Platform (GCP)][gcp] instance is quick and easy.
## Prerequisites
@@ -17,84 +12,52 @@ There are only two prerequisites in order to install GitLab on GCP:
1. You need to sign up for the GCP program. If this is your first time, Google
gives you [$300 credit for free][freetrial] to consume over a 60-day period.
-Once you have performed those two steps, you can visit the
-[GCP launcher console][console] which has a list of all the things you can
-deploy on GCP.
-
-![GCP launcher console](img/gcp_launcher_console_home_page.png)
-
-The next step is to find and install GitLab.
+Once you have performed those two steps, you can [create a VM](#creating-the-vm).
-## Configuring and deploying the VM
+## Creating the VM
To deploy GitLab on GCP you need to follow five simple steps:
-1. Go to https://cloud.google.com/launcher and login with your Google credentials
-1. Search for GitLab from GitLab Inc. (not the same as Bitnami) and click on
- the tile.
+1. Go to https://console.cloud.google.com/compute/instances and login with your Google credentials.
- ![Search for GitLab](img/gcp_search_for_gitlab.png)
+1. Click on **Create**
-1. In the next page, you can see an overview of the GitLab VM as well as some
- estimated costs. Click the **Launch on Compute Engine** button to choose the
- hardware and network settings.
+ ![Search for GitLab](img/launch_vm.png)
- ![Launch on Compute Engine](img/gcp_gitlab_overview.png)
+1. On the next page, you can select the type of VM as well as the
+ estimated costs. Provide the name of the instance, desired datacenter, and machine type. Note that GitLab recommends at least 2 vCPU's and 4GB of RAM.
-1. In the settings page you can choose things like the datacenter where your GitLab
- server will be hosted, the number of CPUs and amount of RAM, the disk size
- and type, etc. Read GitLab's [requirements documentation][req] for more
- details on what to choose depending on your needs.
+ ![Launch on Compute Engine](img/vm_details.png)
- ![Deploy settings](img/new_gitlab_deployment_settings.png)
+1. Click **Change** under Boot disk to select the size, type, and desired operating system. GitLab supports a [variety of linux operating systems][req], including Ubuntu and Debian. Click **Select** when finished.
-1. As a last step, hit **Deploy** when ready. The process will finish in a few
- seconds.
+ ![Deploy in progress](img/boot_disk.png)
- ![Deploy in progress](img/gcp_gitlab_being_deployed.png)
+1. As a last step allow HTTP and HTTPS traffic, then click **Create**. The process will finish in a few seconds.
+## Installing GitLab
-## Visiting GitLab for the first time
+After a few seconds, the instance will be created and available to log in. The next step is to install GitLab onto the instance.
-After a few seconds, GitLab will be successfully deployed and you should be
-able to see the IP address that Google assigned to the VM, as well as the
-credentials to the GitLab admin account.
+![Deploy settings](img/vm_created.png)
-![Deploy settings](img/gitlab_deployed_page.png)
+1. Make a note of the IP address of the instance, as you will need that in a later step.
+1. Click on the SSH button to connect to the instance.
+1. A new window will appear, with you logged into the instance.
-1. Click on the IP under **Site address** to visit GitLab.
-1. Accept the self-signed certificate that Google automatically deployed in
- order to securely reach GitLab's login page.
-1. Use the username and password that are present in the Google console page
- to login into GitLab and click **Sign in**.
+ ![GitLab first sign in](img/ssh_terminal.png)
- ![GitLab first sign in](img/gitlab_first_sign_in.png)
+1. Next, follow the instructions for installing GitLab for the operating system you choose, at https://about.gitlab.com/installation/. You can use the IP address from the step above, as the hostname.
-Congratulations! GitLab is now installed and you can access it via your browser,
-but we're not done yet. There are some steps you need to take in order to have
-a fully functional GitLab installation.
+1. Congratulations! GitLab is now installed and you can access it via your browser. To finish installation, open the URL in your browser and provide the initial administrator password. The username for this account is `root`.
+
+ ![GitLab first sign in](img/first_signin.png)
## Next steps
These are the most important next steps to take after you installed GitLab for
the first time.
-### Changing the admin password and email
-
-Google assigned a random password for the GitLab admin account and you should
-change it ASAP:
-
-1. Visit the GitLab admin page through the link in the Google console under
- **Admin URL**.
-1. Find the Administrator user under the **Users** page and hit **Edit**.
-1. Change the email address to a real one and enter a new password.
-
- ![Change GitLab admin password](img/change_admin_passwd_email.png)
-
-1. Hit **Save changes** for the changes to take effect.
-1. After changing the password, you will be signed out from GitLab. Use the
- new credentials to login again.
-
### Assigning a static IP
By default, Google assigns an ephemeral IP to your instance. It is strongly
@@ -112,7 +75,7 @@ here's how you configure GitLab to be aware of the change:
1. SSH into the VM. You can easily use the **SSH** button in the Google console
and a new window will pop up.
- ![SSH button](img/ssh_via_button.png)
+ ![SSH button](img/vm_created.png)
In the future you might want to set up [connecting with an SSH key][ssh]
instead.
@@ -161,7 +124,6 @@ Kerberos, etc. Here are some documents you might be interested in reading:
- [GitLab Pages configuration](https://docs.gitlab.com/ce/administration/pages/index.html)
- [GitLab Container Registry configuration](https://docs.gitlab.com/ce/administration/container_registry.html)
-[console]: https://console.cloud.google.com/launcher "GCP launcher console"
[freetrial]: https://console.cloud.google.com/freetrial "GCP free trial"
[ip]: https://cloud.google.com/compute/docs/configure-instance-ip-addresses#promote_ephemeral_ip "Configuring an Instance's IP Addresses"
[gcp]: https://cloud.google.com/ "Google Cloud Platform"
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 6eb8890cc4f..4dfc03d0fe0 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -299,9 +299,9 @@ sudo usermod -aG redis git
### Clone the Source
# Clone GitLab repository
- sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-4-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 10-5-stable gitlab
-**Note:** You can change `10-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `10-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
### Configure It
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index 448cbe1077d..1f46ee4c1ea 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -1,9 +1,12 @@
-# Getting started with OpenShift Origin 3 and GitLab
+---
+author: Achilleas Pipinellis
+author_gitlab: axil
+level: intermediary
+article_type: tutorial
+date: 2016-06-28
+---
-> **[Article Type](../../development/writing_documentation.html#types-of-technical-articles):** tutorial ||
-> **Level:** intermediary ||
-> **Author:** [Achilleas Pipinellis](https://gitlab.com/axil) ||
-> **Publication date:** 2016-06-28
+# Getting started with OpenShift Origin 3 and GitLab
## Introduction
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 4324b4ca0b8..b2c9177e6eb 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -192,3 +192,6 @@ use the CI features.
We support the current and the previous major release of Firefox, Chrome/Chromium, Safari and Microsoft browsers (Microsoft Edge and Internet Explorer 11).
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
+in the future because we have features such as Issue Boards which require JavaScript extensively.
diff --git a/doc/integration/openid_connect_provider.md b/doc/integration/openid_connect_provider.md
index 56f367d841e..ad41be52045 100644
--- a/doc/integration/openid_connect_provider.md
+++ b/doc/integration/openid_connect_provider.md
@@ -39,6 +39,7 @@ Currently the following user information is shared with clients:
| `website` | `string` | URL for the user's website
| `profile` | `string` | URL for the user's GitLab profile
| `picture` | `string` | URL for the user's GitLab avatar
+| `groups` | `array` | Names of the groups the user is a member of
[OpenID Connect]: http://openid.net/connect/ "OpenID Connect website"
[doorkeeper-openid_connect]: https://github.com/doorkeeper-gem/doorkeeper-openid_connect "Doorkeeper::OpenidConnect website"
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 01bd925bd6f..5f5ba2b69bc 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -95,7 +95,9 @@ Auto Deploy, and Auto Monitoring will be silently skipped.
The Auto DevOps base domain is required if you want to make use of [Auto
Review Apps](#auto-review-apps) and [Auto Deploy](#auto-deploy). It is defined
-under the project's CI/CD settings while [enabling Auto DevOps](#enabling-auto-devops).
+either under the project's CI/CD settings while
+[enabling Auto DevOps](#enabling-auto-devops) or in instance-wide settings in
+the CI/CD section.
It can also be set at the project or group level as a variable, `AUTO_DEVOPS_DOMAIN`.
A wildcard DNS A record matching the base domain is required, for example,
diff --git a/doc/topics/git/how_to_install_git/index.md b/doc/topics/git/how_to_install_git/index.md
index 7fb578e9ea8..6c909a1ba86 100644
--- a/doc/topics/git/how_to_install_git/index.md
+++ b/doc/topics/git/how_to_install_git/index.md
@@ -1,9 +1,12 @@
-# Installing Git
+---
+author: Sean Packham
+author_gitlab: SeanPackham
+level: beginner
+article_type: user guide
+date: 2017-05-15
+---
-> **[Article Type](../../../development/writing_documentation.html#types-of-technical-articles):** user guide ||
-> **Level:** beginner ||
-> **Author:** [Sean Packham](https://gitlab.com/SeanPackham) ||
-> **Publication date:** 2017-05-15
+# Installing Git
To begin contributing to GitLab projects
you will need to install the Git client on your computer.
diff --git a/doc/topics/git/numerous_undo_possibilities_in_git/index.md b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
index 6a2f7b30dd3..4cb8f083fb5 100644
--- a/doc/topics/git/numerous_undo_possibilities_in_git/index.md
+++ b/doc/topics/git/numerous_undo_possibilities_in_git/index.md
@@ -1,9 +1,12 @@
-# Numerous undo possibilities in Git
+---
+author: Crt Mori
+author_gitlab: Letme
+level: intermediary
+article_type: tutorial
+date: 2017-05-15
+---
-> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
-> **Level:** intermediary ||
-> **Author:** [Crt Mori](https://gitlab.com/Letme) ||
-> **Publication date:** 2017-08-17
+# Numerous undo possibilities in Git
## Introduction
diff --git a/doc/update/10.4-to-10.5.md b/doc/update/10.4-to-10.5.md
new file mode 100644
index 00000000000..313419ed13d
--- /dev/null
+++ b/doc/update/10.4-to-10.5.md
@@ -0,0 +1,361 @@
+---
+comments: false
+---
+
+# From 10.4 to 10.5
+
+Make sure you view this update guide from the tag (version) of GitLab you would
+like to install. In most cases this should be the highest numbered production
+tag (without rc in it). You can select the tag in the version dropdown at the
+top left corner of GitLab (below the menu bar).
+
+If the highest number stable branch is unclear please check the
+[GitLab Blog](https://about.gitlab.com/blog/archives.html) for installation
+guide links by version.
+
+### 1. Stop server
+
+```bash
+sudo service gitlab stop
+```
+
+### 2. Backup
+
+NOTE: If you installed GitLab from source, make sure `rsync` is installed.
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Update Ruby
+
+NOTE: GitLab 9.0 and higher only support Ruby 2.3.x and dropped support for Ruby 2.1.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download and compile Ruby:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.6.tar.gz
+echo '4e6a0f828819e15d274ae58485585fc8b7caace0 ruby-2.3.6.tar.gz' | shasum -c - && tar xzf ruby-2.3.6.tar.gz
+cd ruby-2.3.6
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab now runs [webpack](http://webpack.js.org) to compile frontend assets.
+We require a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+Since 8.17, GitLab requires the use of yarn `>= v0.17.0` to manage
+JavaScript dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 9.2 and higher only supports Go 1.8.3 and dropped support for Go
+1.5.x through 1.7.x. Be sure to upgrade your installation if necessary.
+
+You can check which version you are running with `go version`.
+
+Download and install Go:
+
+```bash
+# Remove former Go installation folder
+sudo rm -rf /usr/local/go
+
+curl --remote-name --progress https://storage.googleapis.com/golang/go1.8.3.linux-amd64.tar.gz
+echo '1862f4c3d3907e59b04a757cfda0ea7aa9ef39274af99a784f5be843c80c6772 go1.8.3.linux-amd64.tar.gz' | shasum -a256 -c - && \
+ sudo tar -C /usr/local -xzf go1.8.3.linux-amd64.tar.gz
+sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
+rm go1.8.3.linux-amd64.tar.gz
+```
+
+### 6. Get latest code
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 10-5-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 10-5-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update MySQL permissions
+
+If you are using MySQL you need to grant the GitLab user the necessary
+permissions on the database:
+
+```bash
+mysql -u root -p -e "GRANT TRIGGER ON \`gitlabhq_production\`.* TO 'git'@'localhost';"
+```
+
+If you use MySQL with replication, or just have MySQL configured with binary logging,
+you will need to also run the following on all of your MySQL servers:
+
+```bash
+mysql -u root -p -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+```
+
+You can make this setting permanent by adding it to your `my.cnf`:
+
+```
+log_bin_trust_function_creators=1
+```
+
+### 11. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There might be configuration options available for [`gitlab.yml`][yaml]. View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/10-4-stable:config/gitlab.yml.example origin/10-5-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+Ensure you're still up-to-date with the latest NGINX configuration changes:
+
+```sh
+cd /home/git/gitlab
+
+# For HTTPS configurations
+git diff origin/10-4-stable:lib/support/nginx/gitlab-ssl origin/10-5-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/10-4-stable:lib/support/nginx/gitlab origin/10-5-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-5-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-5-stable/config/initializers/smtp_settings.rb.sample#L13
+
+#### Init script
+
+There might be new configuration options available for [`gitlab.default.example`][gl-example]. View them with the command below and apply them manually to your current `/etc/default/gitlab`:
+
+```sh
+cd /home/git/gitlab
+
+git diff origin/10-4-stable:lib/support/init.d/gitlab.default.example origin/10-5-stable:lib/support/init.d/gitlab.default.example
+```
+
+Ensure you're still up-to-date with the latest init script changes:
+
+```bash
+cd /home/git/gitlab
+
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+For Ubuntu 16.04.1 LTS:
+
+```bash
+sudo systemctl daemon-reload
+```
+
+### 12. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Optional: clean up old gems
+sudo -u git -H bundle clean
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Compile GetText PO files
+
+sudo -u git -H bundle exec rake gettext:compile RAILS_ENV=production
+
+# Update node dependencies and recompile assets
+sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:compile RAILS_ENV=production NODE_ENV=production
+
+# Clean up cache
+sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+```
+
+**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
+
+### 13. Start application
+
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
+
+### 14. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
+
+To make sure you didn't miss anything run a more thorough check:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (10.4)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 10.3 to 10.4](10.3-to-10.4.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-5-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/10-5-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index b590dfa0d40..ea7b1c9a0ed 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -226,7 +226,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
- Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup:
+ Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
@@ -236,7 +236,7 @@ You can use it to point out a :bug: or warn about :speak_no_evil: patches. And i
If you are new to this, don't be :fearful:. You can easily join the emoji :family:. All you need to do is to look up on the supported codes.
-Consult the [Emoji Cheat Sheet](http://emoji.codes) for a list of all supported emoji codes. :thumbsup:
+Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
### Special GitLab References
@@ -750,7 +750,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also a separate paragraph, but...
This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
-This line is also a separate paragraph, and...
+This line is also a separate paragraph, and...
This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
spaces.
@@ -763,7 +763,7 @@ This line is separated from the one above by two newlines, so it will be a *sepa
This line is also a separate paragraph, but...
This line is only separated by a single newline, so it *does not break* and just follows the previous line in the *same paragraph*.
-This line is also a separate paragraph, and...
+This line is also a separate paragraph, and...
This line is *on its own line*, because the previous line ends with two spaces. (but still in the *same paragraph*)
spaces.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 50a8e0d5ec5..bbe25c2d911 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -5,20 +5,23 @@
Connect your project to Google Kubernetes Engine (GKE) or an existing Kubernetes
cluster in a few steps.
-With a cluster associated to your project, you can use Review Apps, deploy your
-applications, run your pipelines, and much more, in an easy way.
+## Overview
+
+With a Kubernetes cluster associated to your project, you can use
+[Review Apps](../../../ci/review_apps/index.md), deploy your applications, run
+your pipelines, and much more, in an easy way.
There are two options when adding a new cluster to your project; either associate
your account with Google Kubernetes Engine (GKE) so that you can [create new
clusters](#adding-and-creating-a-new-gke-cluster-via-gitlab) from within GitLab,
or provide the credentials to an [existing Kubernetes cluster](#adding-an-existing-kubernetes-cluster).
-## Prerequisites
+## Adding and creating a new GKE cluster via GitLab
-In order to be able to manage your Kubernetes cluster through GitLab, the
-following prerequisites must be met.
+NOTE: **Note:**
+You need Master [permissions] and above to access the Kubernetes page.
-**For a cluster hosted on GKE:**
+Before proceeding, make sure the following requirements are met:
- The [Google authentication integration](../../../integration/google.md) must
be enabled in GitLab at the instance level. If that's not the case, ask your
@@ -28,30 +31,16 @@ following prerequisites must be met.
account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
must be set up and that you have to have permissions to access it.
- You must have Master [permissions] in order to be able to access the
- **Cluster** page.
+ **Kubernetes** page.
- You must have [Cloud Billing API](https://cloud.google.com/billing/) enabled
- You must have [Resource Manager
API](https://cloud.google.com/resource-manager/)
-**For an existing Kubernetes cluster:**
-
-- Since the cluster is already created, there are no prerequisites.
-
----
-
-If all of the above requirements are met, you can proceed to add a new Kubernetes
-cluster.
-
-## Adding and creating a new GKE cluster via GitLab
-
-NOTE: **Note:**
-You need Master [permissions] and above to access the Clusters page.
-
-Before proceeding, make sure all [prerequisites](#prerequisites) are met.
-To add a new cluster hosted on GKE to your project:
+If all of the above requirements are met, you can proceed to create and add a
+new Kubernetes cluster that will be hosted on GKE to your project:
-1. Navigate to your project's **CI/CD > Clusters** page.
-1. Click on **Add cluster**.
+1. Navigate to your project's **CI/CD > Kubernetes** page.
+1. Click on **Add Kubernetes cluster**.
1. Click on **Create with GKE**.
1. Connect your Google account if you haven't done already by clicking the
**Sign in with Google** button.
@@ -66,7 +55,7 @@ To add a new cluster hosted on GKE to your project:
- **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.
- **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster.
-1. Finally, click the **Create cluster** button.
+1. Finally, click the **Create Kubernetes cluster** button.
After a few moments, your cluster should be created. If something goes wrong,
you will be notified.
@@ -77,14 +66,14 @@ enable the Cluster integration.
## Adding an existing Kubernetes cluster
NOTE: **Note:**
-You need Master [permissions] and above to access the Clusters page.
+You need Master [permissions] and above to access the Kubernetes page.
To add an existing Kubernetes cluster to your project:
-1. Navigate to your project's **CI/CD > Clusters** page.
-1. Click on **Add cluster**.
-1. Click on **Add an existing cluster** and fill in the details:
- - **Cluster name** (required) - The name you wish to give the cluster.
+1. Navigate to your project's **CI/CD > Kubernetes** page.
+1. Click on **Add Kuberntes cluster**.
+1. Click on **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.
- **API URL** (required) -
@@ -112,15 +101,13 @@ To add an existing Kubernetes cluster to your project:
- If you or someone created a secret specifically for the project, usually
with limited permissions, the secret's namespace and project namespace may
be the same.
-1. Finally, click the **Create cluster** button.
-
-The Kubernetes service takes the following parameters:
+1. Finally, click the **Create Kuberntes cluster** button.
After a few moments, your cluster should be created. If something goes wrong,
you will be notified.
You can now proceed to install some pre-defined applications and then
-enable the Cluster integration.
+enable the Kubernetes cluster integration.
## Installing applications
@@ -139,7 +126,7 @@ added directly to your configured cluster. Those applications are needed for
NOTE: **Note:**
You need a load balancer installed in your cluster in order to obtain the
external IP address with the following procedure. It can be deployed using the
-**Ingress** application described in the previous section.
+[**Ingress** application](#installing-appplications).
In order to publish your web application, you first need to find the external IP
address associated to your load balancer.
@@ -153,7 +140,8 @@ 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.
-If you installed the Ingress using the **Applications** section, run the following command:
+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} '
@@ -171,9 +159,14 @@ your deployed applications.
## Setting the environment scope
-When adding more than one clusters, you need to differentiate them with an
-environment scope. The environment scope associates clusters and
-[environments](../../../ci/environments.md) in an 1:1 relationship similar to how the
+NOTE: **Note:**
+This is only available for [GitLab Premium][ee] where you can add more than
+one Kubernetes cluster.
+
+When adding more than one Kubernetes clusters to your project, you need to
+differentiate them with an environment scope. The environment scope associates
+clusters and [environments](../../../ci/environments.md) in an 1:1 relationship
+similar to how the
[environment-specific variables](../../../ci/variables/README.md#limiting-environment-scopes-of-secret-variables)
work.
@@ -183,7 +176,7 @@ cluster in a project, and a validation error will occur if otherwise.
---
-For example, let's say the following clusters exist in a project:
+For example, let's say the following Kubernetes clusters exist in a project:
| Cluster | Environment scope |
| ---------- | ------------------- |
@@ -231,8 +224,7 @@ With GitLab Premium, you can associate more than one Kubernetes clusters to your
project. That way you can have different clusters for different environments,
like dev, staging, production, etc.
-To add another cluster, follow the same steps as described in [adding a
-Kubernetes cluster](#adding-a-kubernetes-cluster) and make sure to
+Simply add another cluster, like you did the first time, and make sure to
[set an environment scope](#setting-the-environment-scope) that will
differentiate the new cluster with the rest.
@@ -240,45 +232,42 @@ differentiate the new cluster with the rest.
The Kubernetes cluster integration exposes the following
[deployment variables](../../../ci/variables/README.md#deployment-variables) in the
-GitLab CI/CD build environment:
-
-- `KUBE_URL` - Equal to the API URL.
-- `KUBE_TOKEN` - The Kubernetes token.
-- `KUBE_NAMESPACE` - The Kubernetes namespace is auto-generated if not specified.
- The default value is `<project_name>-<project_id>`. You can overwrite it to
- use different one if needed, otherwise the `KUBE_NAMESPACE` variable will
- receive the default value.
-- `KUBE_CA_PEM_FILE` - Only present if a custom CA bundle was specified. Path
- to a file containing PEM data.
-- `KUBE_CA_PEM` (deprecated) - Only if a custom CA bundle was specified. Raw PEM data.
-- `KUBECONFIG` - Path to a file containing `kubeconfig` for this deployment.
- CA bundle would be embedded if specified.
-
-## Enabling or disabling the Cluster integration
+GitLab CI/CD build environment.
+
+| Variable | Description |
+| -------- | ----------- |
+| `KUBE_URL` | Equal to the API URL. |
+| `KUBE_TOKEN` | The Kubernetes token. |
+| `KUBE_NAMESPACE` | The Kubernetes namespace is auto-generated if not specified. The default value is `<project_name>-<project_id>`. You can overwrite it to use different one if needed, otherwise the `KUBE_NAMESPACE` variable will receive the default value. |
+| `KUBE_CA_PEM_FILE` | Only present if a custom CA bundle was specified. Path to a file containing PEM data. |
+| `KUBE_CA_PEM` | (**deprecated**) Only if a custom CA bundle was specified. Raw PEM data. |
+| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. |
+
+## Enabling or disabling the Kubernetes cluster integration
After you have successfully added your cluster information, you can enable the
-Cluster integration:
+Kubernetes cluster integration:
1. Click the "Enabled/Disabled" switch
1. Hit **Save** for the changes to take effect
You can now start using your Kubernetes cluster for your deployments.
-To disable the Cluster integration, follow the same procedure.
+To disable the Kubernetes cluster integration, follow the same procedure.
-## Removing the Cluster integration
+## Removing the Kubernetes cluster integration
NOTE: **Note:**
-You need Master [permissions] and above to remove a cluster integration.
+You need Master [permissions] and above to remove a Kubernetes cluster integration.
NOTE: **Note:**
When you remove a cluster, you only remove its relation to GitLab, not the
cluster itself. To remove the cluster, you can do so by visiting the GKE
dashboard or using `kubectl`.
-To remove the Cluster integration from your project, simply click on the
+To remove the Kubernetes cluster integration from your project, simply click on the
**Remove integration** button. You will then be able to follow the procedure
-and [add a cluster](#adding-a-cluster) again.
+and add a Kubernetes cluster again.
## What you can get with the Kubernetes integration
diff --git a/doc/user/project/img/label_priority_sort_order.png b/doc/user/project/img/label_priority_sort_order.png
deleted file mode 100644
index 21c7a76a322..00000000000
--- a/doc/user/project/img/label_priority_sort_order.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_assign_label_sidebar.png b/doc/user/project/img/labels_assign_label_sidebar.png
deleted file mode 100644
index d74796fdb4d..00000000000
--- a/doc/user/project/img/labels_assign_label_sidebar.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_assign_label_sidebar_saved.png b/doc/user/project/img/labels_assign_label_sidebar_saved.png
deleted file mode 100644
index dabffe956dc..00000000000
--- a/doc/user/project/img/labels_assign_label_sidebar_saved.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_default.png b/doc/user/project/img/labels_default.png
index 7934e3bfb5e..7a7fab611a4 100644
--- a/doc/user/project/img/labels_default.png
+++ b/doc/user/project/img/labels_default.png
Binary files differ
diff --git a/doc/user/project/img/labels_description_tooltip.png b/doc/user/project/img/labels_description_tooltip.png
deleted file mode 100644
index eea4f8cf0f4..00000000000
--- a/doc/user/project/img/labels_description_tooltip.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_filter.png b/doc/user/project/img/labels_filter.png
deleted file mode 100644
index 6a1ebfc2ecb..00000000000
--- a/doc/user/project/img/labels_filter.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_generate.png b/doc/user/project/img/labels_generate.png
deleted file mode 100644
index 987f4b5be71..00000000000
--- a/doc/user/project/img/labels_generate.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_generate_default.png b/doc/user/project/img/labels_generate_default.png
new file mode 100644
index 00000000000..fca2a06e04f
--- /dev/null
+++ b/doc/user/project/img/labels_generate_default.png
Binary files differ
diff --git a/doc/user/project/img/labels_group_issues.png b/doc/user/project/img/labels_group_issues.png
new file mode 100644
index 00000000000..29dcf7ff45e
--- /dev/null
+++ b/doc/user/project/img/labels_group_issues.png
Binary files differ
diff --git a/doc/user/project/img/labels_list.png b/doc/user/project/img/labels_list.png
new file mode 100644
index 00000000000..12c47ea9766
--- /dev/null
+++ b/doc/user/project/img/labels_list.png
Binary files differ
diff --git a/doc/user/project/img/labels_new_label.png b/doc/user/project/img/labels_new_label.png
deleted file mode 100644
index e26425d0188..00000000000
--- a/doc/user/project/img/labels_new_label.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_new_label_on_the_fly.png b/doc/user/project/img/labels_new_label_on_the_fly.png
deleted file mode 100644
index 2ac9805b1ab..00000000000
--- a/doc/user/project/img/labels_new_label_on_the_fly.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_new_label_on_the_fly_create.png b/doc/user/project/img/labels_new_label_on_the_fly_create.png
deleted file mode 100644
index 02ccf68553b..00000000000
--- a/doc/user/project/img/labels_new_label_on_the_fly_create.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_prioritize.png b/doc/user/project/img/labels_prioritize.png
deleted file mode 100644
index d602a3c90ec..00000000000
--- a/doc/user/project/img/labels_prioritize.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_prioritized.png b/doc/user/project/img/labels_prioritized.png
new file mode 100644
index 00000000000..57dcfe89b3d
--- /dev/null
+++ b/doc/user/project/img/labels_prioritized.png
Binary files differ
diff --git a/doc/user/project/img/labels_promotion.png b/doc/user/project/img/labels_promotion.png
new file mode 100644
index 00000000000..8a5efd210a2
--- /dev/null
+++ b/doc/user/project/img/labels_promotion.png
Binary files differ
diff --git a/doc/user/project/img/labels_sidebar.png b/doc/user/project/img/labels_sidebar.png
new file mode 100644
index 00000000000..7349c6d4f0c
--- /dev/null
+++ b/doc/user/project/img/labels_sidebar.png
Binary files differ
diff --git a/doc/user/project/img/labels_sidebar_assign.png b/doc/user/project/img/labels_sidebar_assign.png
new file mode 100644
index 00000000000..61e8d04fc85
--- /dev/null
+++ b/doc/user/project/img/labels_sidebar_assign.png
Binary files differ
diff --git a/doc/user/project/img/labels_sidebar_inline.png b/doc/user/project/img/labels_sidebar_inline.png
new file mode 100644
index 00000000000..31fa397761d
--- /dev/null
+++ b/doc/user/project/img/labels_sidebar_inline.png
Binary files differ
diff --git a/doc/user/project/img/labels_sort_label_priority.png b/doc/user/project/img/labels_sort_label_priority.png
new file mode 100644
index 00000000000..c8b97639121
--- /dev/null
+++ b/doc/user/project/img/labels_sort_label_priority.png
Binary files differ
diff --git a/doc/user/project/img/labels_sort_priority.png b/doc/user/project/img/labels_sort_priority.png
new file mode 100644
index 00000000000..a95198e7f72
--- /dev/null
+++ b/doc/user/project/img/labels_sort_priority.png
Binary files differ
diff --git a/doc/user/project/img/labels_subscribe.png b/doc/user/project/img/labels_subscribe.png
deleted file mode 100644
index 56f24ae7bc8..00000000000
--- a/doc/user/project/img/labels_subscribe.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/img/labels_subscriptions.png b/doc/user/project/img/labels_subscriptions.png
new file mode 100644
index 00000000000..8bcb3b57f6c
--- /dev/null
+++ b/doc/user/project/img/labels_subscriptions.png
Binary files differ
diff --git a/doc/user/project/img/new_label_from_sidebar.gif b/doc/user/project/img/new_label_from_sidebar.gif
new file mode 100644
index 00000000000..572b29a86e1
--- /dev/null
+++ b/doc/user/project/img/new_label_from_sidebar.gif
Binary files differ
diff --git a/doc/user/project/integrations/bugzilla.md b/doc/user/project/integrations/bugzilla.md
index ba2adc1afda..671804035cc 100644
--- a/doc/user/project/integrations/bugzilla.md
+++ b/doc/user/project/integrations/bugzilla.md
@@ -11,11 +11,7 @@ in the table below.
| `issues_url` | The URL to the issue in Bugzilla project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Bugzilla for the project linked to this GitLab project. Note that the `new_issue_url` requires PRODUCT_NAME to be updated with the product/project name in Bugzilla. |
-Once you have configured and enabled Bugzilla:
-
-- the **Issues** link on the GitLab project pages takes you to the appropriate
- Bugzilla product page
-- clicking **New issue** on the project dashboard takes you to Bugzilla for entering a new issue
+Once you have configured and enabled Bugzilla you'll see the Bugzilla link on the GitLab project pages that takes you to the appropriate Bugzilla project.
## Referencing issues in Bugzilla
diff --git a/doc/user/project/integrations/custom_issue_tracker.md b/doc/user/project/integrations/custom_issue_tracker.md
index 757522c2ae3..731291ebe84 100644
--- a/doc/user/project/integrations/custom_issue_tracker.md
+++ b/doc/user/project/integrations/custom_issue_tracker.md
@@ -13,6 +13,8 @@ in the table below.
| `issues_url` | The URL to the issue in the issue tracker project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. For example, `https://customissuetracker.com/project-name/:id`. |
| `new_issue_url` | Currently unused. Will be changed in a future release. |
+Once you have configured and enabled Custom Issue Tracker Service you'll see a link on the GitLab project pages that takes you to that custom issue tracker.
+
## Referencing issues
diff --git a/doc/user/project/integrations/img/prometheus_dashboard.png b/doc/user/project/integrations/img/prometheus_dashboard.png
new file mode 100644
index 00000000000..bd19f1b44cc
--- /dev/null
+++ b/doc/user/project/integrations/img/prometheus_dashboard.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_deploy.png b/doc/user/project/integrations/img/prometheus_deploy.png
new file mode 100644
index 00000000000..d39081bcc7b
--- /dev/null
+++ b/doc/user/project/integrations/img/prometheus_deploy.png
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_gcp_firewall_rule.png b/doc/user/project/integrations/img/prometheus_gcp_firewall_rule.png
deleted file mode 100644
index e30cba211e6..00000000000
--- a/doc/user/project/integrations/img/prometheus_gcp_firewall_rule.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/img/prometheus_gcp_node_name.png b/doc/user/project/integrations/img/prometheus_gcp_node_name.png
deleted file mode 100644
index ea289431454..00000000000
--- a/doc/user/project/integrations/img/prometheus_gcp_node_name.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index f77569e4886..fc527663db0 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -116,7 +116,7 @@ in the table below.
| `Transition ID` | This is the ID of a transition that moves issues to a closed state. You can find this number under JIRA workflow administration ([see screenshot](img/jira_workflow_screenshot.png)). **Closing JIRA issues via commits or Merge Requests won't work if you don't set the ID correctly.** |
After saving the configuration, your GitLab project will be able to interact
-with all JIRA projects in your JIRA instance.
+with all JIRA projects in your JIRA instance and you'll see the JIRA link on the GitLab project pages that takes you to the appropriate JIRA project.
![JIRA service page](img/jira_service_page.png)
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index 5fefb3b69c4..249463fb86e 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -2,119 +2,69 @@
> [Introduced][ce-8935] in GitLab 9.0.
-GitLab offers powerful integration with [Prometheus] for monitoring your apps.
-Metrics are retrieved from the configured Prometheus server, and then displayed
+GitLab offers powerful integration with [Prometheus] for monitoring key metrics your apps, directly within GitLab.
+Metrics for each environment are retrieved from Prometheus, and then displayed
within the GitLab interface.
-Each project can be configured with its own specific Prometheus server, see the
-[configuration](#configuration) section for more details. If you have a single
-Prometheus server which monitors all of your infrastructure, you can pre-fill
-the settings page with a default template. To configure the template, see the
-[Services templates](services_templates.md) document.
+![Environment Dashboard](img/prometheus_dashboard.png)
-## Requirements
+There are two ways to setup Prometheus integration, depending on where your apps are running:
+* 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).
-Integration with Prometheus requires the following:
-
-1. GitLab 9.0 or higher
-1. Prometheus must be configured to collect one of the [supported metrics](prometheus_library/metrics.md)
-1. Each metric must be have a label to indicate the environment
-1. GitLab must have network connectivity to the Prometheus server
-
-## Getting started with Prometheus monitoring
+## Managed Prometheus on Kubernetes
+> **Note**: [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/28916) in GitLab 10.5
-Depending on your deployment and where you have located your GitLab server, there are a few options to get started with Prometheus monitoring.
+GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cluster](../clusters/index.md), making monitoring of your apps easy.
-* If both GitLab and your applications are installed in the same Kubernetes cluster, you can leverage the [bundled Prometheus server within GitLab](#configuring-omnibus-gitlab-prometheus-to-monitor-kubernetes).
-* If your applications are deployed on Kubernetes, but GitLab is not in the same cluster, then you can [configure a Prometheus server in your Kubernetes cluster](#configuring-your-own-prometheus-server-within-kubernetes).
-* If your applications are not running in Kubernetes, [get started with Prometheus](#getting-started-with-prometheus-outside-of-kubernetes).
-
-### Getting started with Prometheus outside of Kubernetes
-
-Installing and configuring Prometheus to monitor applications is fairly straight forward.
-
-1. [Install Prometheus](https://prometheus.io/docs/introduction/install/)
-1. Set up one of the [supported monitoring targets](prometheus_library/metrics.md)
-1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config)
+### Requirements
-### Configuring Omnibus GitLab Prometheus to monitor Kubernetes deployments
+* A [connected Kubernetes cluster](../clusters/index.md)
+* Helm Tiller [installed by GitLab](../clusters/index.md#installing-applications)
-With Omnibus GitLab running inside of Kubernetes, you can leverage the bundled
-version of Prometheus to collect the supported metrics. Once enabled, Prometheus will automatically begin monitoring Kubernetes Nodes and any [annotated Pods](https://prometheus.io/docs/operating/configuration/#<kubernetes_sd_config>).
+### Getting started
-1. Read how to configure the bundled Prometheus server in the
- [Administration guide][gitlab-prometheus-k8s-monitor].
-1. Now that Prometheus is configured, proceed on
- [configuring the Prometheus project service in GitLab](#configuration-in-gitlab).
+Once you have a connected Kubernetes cluster with Helm installed, deploying a managed Prometheus is as easy as a single click.
-### Configuring your own Prometheus server within Kubernetes
+1. Go to the `CI/CD > Kubernetes` page, to view your connected clusters
+1. Select the cluster you would like to deploy Prometheus to
+1. Click the **Install** button to deploy Prometheus to the cluster
-Setting up and configuring Prometheus within Kubernetes is quick and painless.
-The Prometheus project provides an [official Docker image][prometheus-docker-image]
-which we can use as a starting point.
+![Managed Prometheus Deploy](img/prometheus_deploy.png)
-To get started quickly, we have provided a [sample YML file][prometheus-yml]
-that can be used as a template. This file will create a `prometheus` **Namespace**,
-**Service**, **Deployment**, and **ConfigMap** in Kubernetes. You can upload
-this file to the Kubernetes dashboard using **+ Create** at the top right.
+### About managed Prometheus deployments
-![Deploy Prometheus](img/prometheus_yaml_deploy.png)
+Prometheus is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/prometheus). Prometheus is only accessible within the cluster, with GitLab communicating through the [Kubernetes API](https://kubernetes.io/docs/concepts/overview/kubernetes-api/).
-Or use `kubectl`:
+The Prometheus server will [automatically detect and monitor](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#%3Ckubernetes_sd_config%3E) nodes, pods, and endpoints. To configure a resource to be monitored by Prometheus, simply set the following [Kubernetes annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/):
+* `prometheus.io/scrape` to `true` to enable monitoring of the resource.
+* `prometheus.io/port` to define the port of the metrics endpoint.
+* `prometheus.io/path` to define the path of the metrics endpoint. Defaults to `/metrics`.
-```bash
-kubectl apply -f path/to/prometheus.yml
-```
+CPU and Memory consumption is monitored, but requires [naming conventions](prometheus_library/kubernetes.html#specifying-the-environment) in order to determine the environment. If you are using [Auto DevOps](../../../topics/autodevops/), this is handled automatically.
-Once deployed, you should see the Prometheus service, deployment, and
-pod start within the `prometheus` namespace. The server will begin to collect
-metrics from each Kubernetes Node in the cluster, based on the configuration
-provided in the template. It will also attempt to collect metrics from any Kubernetes Pods that have been [annotated for Prometheus](https://prometheus.io/docs/operating/configuration/#pod).
+The [NGINX Ingress](../clusters/index.md#installing-applications) that is deployed by GitLab to clusters, is automatically annotated for monitoring providing key response metrics: latency, throughput, and error rates.
-Since GitLab is not running within Kubernetes, the template provides external
-network access via a `NodePort` running on `30090`. This method allows access
-to be controlled using provider firewall rules, like within Google Compute Engine.
+## Manual configuration of Prometheus
-Since a `NodePort` does not automatically have firewall rules created for it,
-one will need to be created manually to allow access. In GCP/GKE, you will want
-to confirm the Node that the Prometheus pod is running on. This can be done
-either by looking at the Pod in the Kubernetes dashboard, or by running:
+### Requirements
-```bash
-kubectl describe pods -n prometheus
-```
-
-Next on GKE, we need to get the `tag` of the Node or VM Instance, so we can
-create an accurate firewall rule. The easiest way to do this is to go into the
-Google Cloud Platform Compute console and select the VM instance that matches
-the name of the Node gathered from the step above. In this case, the node tag
-needed is `gke-prometheus-demo-5d5ada10-node`. Also make a note of the
-**External IP**, which will be the IP address the Prometheus server is reachable
-on.
-
-![GCP Node Detail](img/prometheus_gcp_node_name.png)
-
-Armed with the proper Node tag, the firewall rule can now be created
-specifically for this node. To create the firewall rule, open the Google Cloud
-Platform Networking console, and select **Firewall Rules**.
-
-Create a new rule:
+Integration with Prometheus requires the following:
-- Specify the source IP range to match your desired access list, which should
- include your GitLab server. A sample of GitLab.com's IP address range is
- available [in this issue][gitlab.com-ip-range], but note that GitLab.com's IPs
- are subject to change without prior notification.
-- Allowed protocol and port should be `tcp:30090`.
-- The target tags should match the Node tag identified earlier in this step.
+1. GitLab 9.0 or higher
+1. Prometheus must be configured to collect one of the [supported metrics](prometheus_library/metrics.md)
+1. Each metric must be have a label to indicate the environment
+1. GitLab must have network connectivity to the Prometheus server
-![GCP Firewall Rule](img/prometheus_gcp_firewall_rule.png)
+### Getting started
----
+Installing and configuring Prometheus to monitor applications is fairly straight forward.
-Now that Prometheus is configured, proceed to
-[configure the Prometheus project service in GitLab](##configuration-in-gitlab).
+1. [Install Prometheus](https://prometheus.io/docs/introduction/install/)
+1. Set up one of the [supported monitoring targets](prometheus_library/metrics.md)
+1. Configure the Prometheus server to [collect their metrics](https://prometheus.io/docs/operating/configuration/#scrape_config)
-## Configuration in GitLab
+### Configuration in GitLab
The actual configuration of Prometheus integration within GitLab is very simple.
All you will need is the DNS or IP address of the Prometheus server you'd like
diff --git a/doc/user/project/integrations/prometheus_library/kubernetes.md b/doc/user/project/integrations/prometheus_library/kubernetes.md
index a6673fa2a00..02adc562028 100644
--- a/doc/user/project/integrations/prometheus_library/kubernetes.md
+++ b/doc/user/project/integrations/prometheus_library/kubernetes.md
@@ -24,9 +24,10 @@ Prometheus server up and running. You have two options here:
- If you have an Omnibus based GitLab installation within your Kubernetes cluster, you can leverage the bundled Prometheus server to [monitor Kubernetes](../../../../administration/monitoring/prometheus/index.md#configuring-prometheus-to-monitor-kubernetes).
- To configure your own Prometheus server, you can follow the [Prometheus documentation](https://prometheus.io/docs/introduction/overview/) or [our guide](../../../../administration/monitoring/prometheus/index.md#configuring-your-own-prometheus-server-within-kubernetes).
-## Specifying the Environment label
+## Specifying the Environment
-In order to isolate and only display relevant metrics for a given environment
-however, GitLab needs a method to detect which labels are associated. To do this, GitLab will [look for an `environment` label](metrics.md#identifying-environments).
+In order to isolate and only display relevant CPU and Memory metrics for a given environment, GitLab needs a method to detect which containers it is running. Because these metrics are tracked at the container level, traditional Kubernetes labels are not available.
+
+Instead, the [Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) or [DaemonSet](https://kubernetes.io/docs/concepts/workloads/controllers/daemonset/) name should begin with [CI_ENVIRONMENT_SLUG](../../../../ci/variables/README.md#predefined-variables-environment-variables). It can be followed by a `-` and additional content if desired. For example, a deployment name of `review-homepage-5620p5` would match the `review/homepage` environment.
If you are using [GitLab Auto-Deploy](../../../../ci/autodeploy/index.md) and one of the two [provided Kubernetes monitoring solutions](../prometheus.md#getting-started-with-prometheus-monitoring), the `environment` label will be automatically added.
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
index e6f13d0630b..49b34c82ae6 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
@@ -2,11 +2,11 @@
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/13438) in GitLab 9.5
-GitLab has support for automatically detecting and monitoring the Kubernetes NGINX ingress controller. This is provided by leveraging the built in Prometheus metrics included in [version 0.9.0](https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta1) of the ingress.
+GitLab has support for automatically detecting and monitoring the Kubernetes NGINX ingress controller. This is provided by leveraging the built in Prometheus metrics included in [version 0.9.0](https://github.com/kubernetes/ingress/blob/master/controllers/nginx/Changelog.md#09-beta1) and above of the ingress.
## Requirements
-The [Prometheus service](../prometheus/index.md) must be enabled.
+[Prometheus integration](../prometheus/index.md) must be active.
## Metrics supported
@@ -18,24 +18,34 @@ The [Prometheus service](../prometheus/index.md) must be enabled.
## Configuring NGINX ingress monitoring
-If you have deployed with the [gitlab-omnibus](https://docs.gitlab.com/ee/install/kubernetes/gitlab_omnibus.md) Helm chart, and your application is running in the same cluster, no further action is required. The ingress metrics will be automatically enabled and annotated for Prometheus monitoring. Simply ensure Prometheus monitoring is [enabled for your project](../prometheus.md), which is on by default.
+If you have deployed NGINX Ingress using GitLab's [Kubernetes cluster integration](../../clusters/index.md#installing-applications), it will [automatically be monitored](#about-managed-nginx-ingress-deployments) by Prometheus.
-For other deployments, there is some configuration required depending on your installation:
-* NGINX Ingress should be version 0.9.0 or above
+For other deployments, there is [some configuration](#manually-setting-up-nginx-ingress-for-prometheus-monitoring) required depending on your installation:
+* NGINX Ingress should be version 0.9.0 or above, with metrics enabled
* NGINX Ingress should be annotated for Prometheus monitoring
* Prometheus should be configured to monitor annotated pods
-### Setting up NGINX Ingress for Prometheus monitoring
+### 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](https://docs.gitlab.com/ce/user/project/clusters/index.html#getting-the-external-ip-address).
+
+NGINX is configured for Prometheus monitoring, by setting:
+* `enable-vts-status: "true"`, to export Prometheus metrics
+* `prometheus.io/scrape: "true"`, to enable automatic discovery
+* `prometheus.io/port: "10254"`, to specify the metrics port
+
+When used in conjunction with the GitLab deployed Prometheus service, response metrics will be automatically collected.
+
+### Manually setting up NGINX Ingress for Prometheus monitoring
Version 0.9.0 and above of [NGINX ingress](https://github.com/kubernetes/ingress/tree/master/controllers/nginx) have built-in support for exporting Prometheus metrics. To enable, a ConfigMap setting must be passed: `enable-vts-status: "true"`. Once enabled, a Prometheus metrics endpoint will start running on port 10254.
-With metric data now available, Prometheus needs to be configured to collect it. The easiest way to do this is to leverage Prometheus' [built-in Kubernetes service discovery](https://prometheus.io/docs/operating/configuration/#kubernetes_sd_config), which automatically detects a variety of Kubernetes components and makes them available for monitoring. Since NGINX ingress metrics are exposed per pod, a scrape job for Kubernetes pods is required. A sample pod scraping configuration [is available](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml#L248). This configuration will detect pods and enable collection of metrics **only if** they have been specifically annotated for monitoring.
+Next, the ingress needs to be annotated for Prometheus monitoring. Two new annotations need to be added:
-Depending on how NGINX ingress was deployed, typically a DaemonSet or Deployment, edit the corresponding YML spec. Two new annotations need to be added:
* `prometheus.io/scrape: "true"`
* `prometheus.io/port: "10254"`
-Prometheus should now be collecting NGINX ingress metrics. To validate view the Prometheus Targets, available under `Status > Targets` on the Prometheus dashboard. New entries for NGINX should be listed in the kubernetes pod monitoring job, `kubernetes-pods`.
+Managing these settings depends on how NGINX ingress has been deployed. If you have deployed via the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress), metrics can be enabled with `controller.stats.enabled` along with the required annotations. Alternatively it is possible edit the NGINX ingress YML directly in the [Kubernetes dashboard](https://github.com/kubernetes/dashboard).
## Specifying the Environment label
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index cc3218fbfd1..de2cf6d4647 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -12,6 +12,8 @@ in the table below.
| `issues_url` | The URL to the issue in Redmine project that is linked to this GitLab project. Note that the `issues_url` requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
| `new_issue_url` | This is the URL to create a new issue in Redmine for the project linked to this GitLab project. **This is currently not being used and will be removed in a future release.** |
+ Once you have configured and enabled Redmine you'll see the Redmine link on the GitLab project pages that takes you to the appropriate Redmine project.
+
As an example, below is a configuration for a project named gitlab-ci.
![Redmine configuration](img/redmine_configuration.png)
diff --git a/doc/user/project/integrations/samples/prometheus.yml b/doc/user/project/integrations/samples/prometheus.yml
deleted file mode 100644
index 3a4735d282f..00000000000
--- a/doc/user/project/integrations/samples/prometheus.yml
+++ /dev/null
@@ -1,107 +0,0 @@
-apiVersion: v1
-kind: Namespace
-metadata:
- name: prometheus
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: prometheus
- namespace: prometheus
-data:
- prometheus.yml: |-
- scrape_configs:
- - job_name: 'kubernetes-nodes'
- scheme: https
- tls_config:
- ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
- insecure_skip_verify: true
- bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
- kubernetes_sd_configs:
- - role: node
- metric_relabel_configs:
- - source_labels: [pod_name]
- target_label: environment
- regex: (.+)-.+-.+
- replacement: $1
- - job_name: kubernetes-pods
- tls_config:
- ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
- insecure_skip_verify: true
- bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
- kubernetes_sd_configs:
- - role: pod
- api_server: https://kubernetes.default.svc:443
- tls_config:
- ca_file: "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
- bearer_token_file: "/var/run/secrets/kubernetes.io/serviceaccount/token"
- relabel_configs:
- - source_labels:
- - __meta_kubernetes_pod_annotation_prometheus_io_scrape
- action: keep
- regex: 'true'
- - source_labels:
- - __meta_kubernetes_pod_annotation_prometheus_io_path
- action: replace
- target_label: __metrics_path__
- regex: "(.+)"
- - source_labels:
- - __address__
- - __meta_kubernetes_pod_annotation_prometheus_io_port
- action: replace
- regex: "([^:]+)(?::[0-9]+)?;([0-9]+)"
- replacement: "$1:$2"
- target_label: __address__
- - action: labelmap
- regex: __meta_kubernetes_pod_label_(.+)
- - source_labels:
- - __meta_kubernetes_namespace
- action: replace
- target_label: kubernetes_namespace
- - source_labels:
- - __meta_kubernetes_pod_name
- action: replace
- target_label: kubernetes_pod_name
----
-apiVersion: v1
-kind: Service
-metadata:
- name: prometheus
- namespace: prometheus
-spec:
- selector:
- app: prometheus
- ports:
- - name: prometheus
- protocol: TCP
- port: 9090
- nodePort: 30090
- type: NodePort
----
-apiVersion: extensions/v1beta1
-kind: Deployment
-metadata:
- name: prometheus
- namespace: prometheus
-spec:
- replicas: 1
- template:
- metadata:
- labels:
- app: prometheus
- spec:
- containers:
- - name: prometheus
- image: prom/prometheus:latest
- args:
- - '--config.file=/prometheus-data/prometheus.yml'
- ports:
- - name: prometheus
- containerPort: 9090
- volumeMounts:
- - name: data-volume
- mountPath: /prometheus-data
- volumes:
- - name: data-volume
- configMap:
- name: prometheus
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 88acd8edbe2..be4436749f9 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -64,9 +64,7 @@ You can also [search and filter](../../search/index.md#issues-and-merge-requests
### Issues per group
-View all the issues in a group (that is, all the issues across all projects in that
-group) by navigating to **Group > Issues**. This view also has the open and closed
-issue tabs.
+View issues in all projects in the group, including all projects of all descendant subgroups of the group. Navigate to **Group > Issues** to view these issues. This view also has the open and closed issues tabs.
![Group Issues list view](img/group_issues_list_view.png)
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index 0bef83d18e8..f2ca6a6822e 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -50,8 +50,8 @@ Often multiple people likely work on the same issue together,
which can especially be difficult to track in large teams
where there is shared ownership of an issue.
-In GitLab Enterprise Edition, you can also select multiple assignees
-to an issue.
+In [GitLab Starter](https://about.gitlab.com/products/), you can also
+select multiple assignees to an issue.
Learn more on the [Multiple Assignees documentation](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html).
diff --git a/doc/user/project/labels.md b/doc/user/project/labels.md
index d7eb4bca89c..dabffaec5fa 100644
--- a/doc/user/project/labels.md
+++ b/doc/user/project/labels.md
@@ -1,174 +1,125 @@
# Labels
-Labels provide an easy way to categorize the issues or merge requests based on
-descriptive titles like `bug`, `documentation` or any other text you feel like.
-They can have different colors, a description, and are visible throughout
-the issue tracker or inside each issue individually.
+## Overview
-With labels, you can navigate the issue tracker and filter any bloated
-information to visualize only the issues you are interested in. Let's see how
-that works.
+Labels allow you to categorize issues or merge requests using descriptive titles like `bug`, `feature request`, or `docs`. Each label also has a customizable color. They allow you to quickly and dynamically filter and manage issues or merge requests you care about, and are visible throughout GitLab in most places where issues and merge requests are located.
-## Create new labels
+## Project labels and group labels
+
+In GitLab, you can create project and group labels:
+
+- **Project labels** can be assigned to issues or merge requests in that project only.
+- **Group labels** can be assigned to any issue or merge request of any project in that group.
+- In the [future](https://gitlab.com/gitlab-org/gitlab-ce/issues/40915), you will be able to assign group labels to issues and merge reqeusts of projects in [subgroups](../group/subgroups/index.md).
+
+## Creating labels
>**Note:**
-A permission level of `Developer` or higher is required in order to manage
-labels.
+A permission level of `Developer` or higher is required to create labels.
-Head over a single project and navigate to **Issues > Labels**.
+### New project label
-The first time you visit this page, you'll notice that there are no labels
-created yet.
+To create a **project label**, navigate to **Issues > Labels** in the project.
-Creating a new label from scratch is as easy as pressing the **New label**
-button. From there on you can choose the name, give it an optional description,
-a color and you are set.
+Click the **New label** button. Enter the title, an optional description, and the background color. Click **Create label** to create the label.
-When you are ready press the **Create label** button to create the new label.
+If a project has no labels, you can generate a default set of project labels from its empty label list page:
-![New label](img/labels_new_label.png)
+![Labels generate default](img/labels_generate_default.png)
----
+GitLab will add the following default labels to the project:
-## Default labels
+![Labels default](img/labels_default.png)
-The very first time you visit the labels area, it's gonna be empty. In that
-case, it's possible to populate the labels for your project from a set of
-predefined labels.
+### New group label
-Click the link to 'Generate a default set of labels' and GitLab will
-generate them for you. There are 8 default generated labels in total:
+To create a **group label**, follow similar steps from above to project labels. Navigate to **Issues > Labels** in the group and create it from there.
-- bug
-- confirmed
-- critical
-- discussion
-- documentation
-- enhancement
-- suggestion
-- support
+Group labels appear in every label list page of the group's child projects.
-## Labels Overview
+![Labels list](img/labels_list.png)
-![Default generated labels](img/labels_default.png)
+### New project label from sidebar
-You can see that from the labels page you can have an overview of the number of
-issues and merge requests assigned to each label.
+From the sidebar of an issue or a merge request, you can create a create a new **project label** inline immediately, instead of navigating to the project label list page.
-## Prioritize labels
+![Labels inline](img/new_label_from_sidebar.gif)
->**Notes:**
->
-> - Introduced in GitLab 8.9.
-> - Priority sorting is based on the highest priority label only. This might
-> change in the future, follow the discussion in
-> https://gitlab.com/gitlab-org/gitlab-ce/issues/18554.
+## Editing labels
-Prioritized labels are like any other label, but sorted by priority. This allows
-you to sort issues and merge requests by label priority.
+NOTE: **Note:**
+A permission level of `Developer` or higher is required to edit labels.
-To prioritize labels, navigate to your project's **Issues > Labels** and click
-on the star icon next to them to put them in the priority list. Click on the
-star icon again to remove them from the list.
+You can update a label by navigating to **Issues > Labels** in the project or group and clicking the pencil icon.
-From there, you can drag them around to set the desired priority. Priority is
-set from high to low with an ascending order. Labels with no priority, count as
-having their priority set to null.
+You can delete a label by clicking the trash icon.
-![Prioritize labels](img/labels_prioritize.png)
+### Promoting project labels to group labels
-Now that you have labels prioritized, you can use the 'Label priority' and 'Priority'
-sort orders in the issues or merge requests tracker.
+If you are expanding from a few projects to a larger number of projects within the same group, you may want to share the same label among multiple projects in the same group. If you previously created a project label and now want to make it available for other projects, you can promote it to a group label.
-In the following, everything applies to both issues and merge requests, but we'll
-refer to just issues for brevity.
+From the project label list page, you can promote a project label to a group label. This will merge all project labels across all projects in this group with the same name into a single group label. All issues and merge requests that previously were assigned one of these project labels will now be assigned the new group label. This action cannot be reversed and the changes are permanent.
-The 'Label priority' sort order positions issues with higher priority labels
-toward the top, and issues with lower priority labels toward the bottom. A non-prioritized
-label is considered to have the lowest priority. For a given issue, we _only_ consider the
-highest priority label assigned to it in the comparison. ([We are discussing](https://gitlab.com/gitlab-org/gitlab-ce/issues/18554)
-including all the labels in a given issue for this comparison.) Given two issues
-are equal according to this sort comparison, their relative order is equal, and
-therefore it's not guaranteed that one will be always above the other.
+![Labels promotion](img/labels_promotion.png)
-![Label priority sort order](img/label_priority_sort_order.png)
+## Assigning labels from the sidebar
-The 'Priority' sort order comparison first considers an issue's milestone's due date,
-(if the issue is assigned a milestone and the milestone's due date exists), and then
-secondarily considers the label priority comparison above. Sooner due dates results
-a higher sort order. If an issue doesn't have a milestone due date, it is equivalent to
-being assigned to a milestone that has a due date in the infinite future. Given two issues
-are equal according to this two-stage sort comparison, their relative order is equal, and
-therefore it's not guaranteed that one will be always above the other.
+Every issue and merge request can be assigned any number of labels. The labels are visible on every issue and merge request page, in the sidebar. They are also visible in the issue board. From the sidebar, you can assign or unassign a label to the object (i.e. label or unlabel it). You can also perform this as a [quick action](quick_actions.md) in a comment.
-![Priority sort order](img/priority_sort_order.png)
+| View labels in sidebar | Assign labels from sidebar |
+|:---:|:---:|
+| ![Labels sidebar](img/labels_sidebar.png) | ![Labels sidebar assign](img/labels_sidebar_assign.png) |
+## Filtering issues and merge requests by label
-## Subscribe to labels
+### Filtering in list pages
-If you don’t want to miss issues or merge requests that are important to you,
-simply subscribe to a label. You’ll get notified whenever the label gets added
-to an issue or merge request, making sure you don’t miss a thing.
+From the project issue list page and the project merge request list page, you can [filter](../search/index.md#issues-and-merge-requests) by both group labels and project labels.
-Go to your project's **Issues > Labels** area, find the label(s) you want to
-subscribe to and click on the eye icon. Click again to unsubscribe.
+From the group issue list page and the group merge request list page, you can [filter](../search/index.md#issues-and-merge-requests) by both group labels and project labels.
-![Subscribe to labels](img/labels_subscribe.png)
+![Labels group issues](img/labels_group_issues.png)
-If you work on a large or popular project, try subscribing only to the labels
-that are relevant to you. You’ll notice it’ll be much easier to focus on what’s
-important.
+### Filtering in issue boards
-## Create a new label when inside an issue
+- From [project boards](issue_board.md), you can filter by both group labels and project labels in the [search and filter bar](../search/index.md#issue-boards).
-There are times when you are already inside an issue searching to assign a
-label, only to realize it doesn't exist. Instead of going to the **Labels**
-page and being distracted from your original purpose, you can create new
-labels on the fly.
+## Subscribing to labels
-Expand the issue sidebar and select **Create new label** from the labels dropdown
-list. Provide a name, pick a color and hit **Create**. The new label will be
-ready to used right away!
+From the project label list page and the group label list page, you can subscribe to [notifications](../../workflow/notifications.md) of a given label, to alert you that that label has been assigned to an issue or merge request.
-![New label on the fly](img/labels_new_label_on_the_fly.png)
+![Labels subscriptions](img/labels_subscriptions.png)
-## Assigning labels to issues and merge requests
+## Label priority
-There are generally two ways to assign a label to an issue or merge request.
+>**Notes:**
+>
+> - Introduced in GitLab 8.9.
+> - Priority sorting is based on the highest priority label only. [This discussion](https://gitlab.com/gitlab-org/gitlab-ce/issues/18554) considers changing this.
-The first one is to assign a label when you first create or edit an issue or
-merge request.
+Labels can have relative priorities, which are used in the "Label priority" and "Priority" sort orders of the issue and merge request list pages.
-The second way is by using the right sidebar when inside an issue or merge
-request. Expand it and hit **Edit** in the labels area. Start typing the name
-of the label you are looking for to narrow down the list, and select it. You
-can add more than one labels at once. When done, click outside the sidebar area
-for the changes to take effect.
+From the project label list page, star a label to indicate that it has a priority. Drag starred labels up and down to change their priority. Higher means higher priority. Prioritization happens at the project level, only on the project label list page, and not on the group label list page. However, both project and group labels can be prioritized on the project label list page since both types are displayed on the project label list page.
-![Assign label in sidebar](img/labels_assign_label_sidebar.png)
-![Save labels in sidebar](img/labels_assign_label_sidebar_saved.png)
+![Labels prioritized](img/labels_prioritized.png)
----
+On the project and group issue and merge request list pages, you can sort by `Label priority` and `Priority`, which account for objects (issues and merge requests) that have prioritized labels assigned to them.
-To remove labels, expand the left sidebar and unmark them from the labels list.
-Simple as that.
+If you sort by `Label priority`, GitLab considers this sort comparison order:
-## Use labels to filter issues
+- Object with a higher priority prioritized label.
+- Object without a prioritized label.
-Once you start adding labels to your issues, you'll see the benefit of it.
-Labels can have several uses, one of them being the quick filtering of issues
-or merge requests.
+Ties are broken arbitrarily. (Note that we _only_ consider the highest prioritized label in an object, and not any of the lower prioritized labels. [This discussion](https://gitlab.com/gitlab-org/gitlab-ce/issues/18554) considers changing this.)
-Pick an existing label from the dropdown _Label_ menu or click on an existing
-label from the issue tracker. In the latter case, you also get to see the
-label description like shown below.
+![Labels sort label priority](img/labels_sort_label_priority.png)
-![Filter labels](img/labels_filter.png)
+If you sort by `Priority`, GitLab considers this sort comparison order:
----
+- Object's assigned [milestone](milestones/index.md)'s due date is sooner, provided the object has a milestone and the milestone has a due date. If this isn't the case, consider the object having a due date in the infinite future.
+- Object with a higher priority prioritized label.
+- Object without a prioritized label.
-And if you added a description to your label, you can see it by hovering your
-mouse over the label in the issue tracker or wherever else the label is
-rendered.
+Ties are broken arbitrarily.
-![Label tooltips](img/labels_description_tooltip.png)
+![Labels sort priority](img/labels_sort_priority.png)
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index aa3266cb457..0de89f90e21 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -70,9 +70,9 @@ and you can use the tabs available to quickly filter by open and closed. You can
## Merge requests per group
-View all the merge requests in a group (that is, all the merge requests across all projects in that
-group) by navigating to **Group > Merge Requests**. This view also has the open, merged, and closed
-merge request tabs, from which you can [search and filter the results](../../search/index.md#issues-and-merge-requests-per-group).
+View merge requests in all projects in the group, including all projects of all descendant subgroups of the group. Navigate to **Group > Merge Requests** to view these merge requests. This view also has the open and closed merge requests tabs.
+
+You can [search and filter the results](../../search/index.md#issues-and-merge-requests-per-group) from here.
![Group Issues list view](img/group_merge_requests_list_view.png)
@@ -146,6 +146,19 @@ administrator to do so.
![Create new merge requests by email](img/create_from_email.png)
+## Find the merge request that introduced a change
+
+> **Note**: this feature was [implemented in GitLab 10.5](https://gitlab.com/gitlab-org/gitlab-ce/issues/2383).
+
+When viewing the commit details page, GitLab will link to the merge request (or
+merge requests, if it's in more than one) containing that commit.
+
+This only applies to commits that are in the most recent version of a merge
+request - if a commit was in a merge request, then rebased out of that merge
+request, they will not be linked.
+
+[Read more about merge request versions](versions.md)
+
## Revert changes
GitLab implements Git's powerful feature to revert any commit with introducing
@@ -160,7 +173,7 @@ of merge request diff is created. When you visit a merge request that contains
more than one pushes, you can select and compare the versions of those merge
request diffs.
-[Read more about the merge requests versions.](versions.md)
+[Read more about merge request versions](versions.md)
## Work In Progress merge requests
diff --git a/doc/user/project/merge_requests/work_in_progress_merge_requests.md b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
index 546c8bdc5e5..f01da06fa6e 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
@@ -7,7 +7,8 @@ have been marked a **Work In Progress**.
![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:`.
+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)
diff --git a/doc/user/project/milestones/img/milestone_create.png b/doc/user/project/milestones/img/milestone_create.png
deleted file mode 100644
index beb2caa897f..00000000000
--- a/doc/user/project/milestones/img/milestone_create.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/milestones/img/milestone_group_create.png b/doc/user/project/milestones/img/milestone_group_create.png
deleted file mode 100644
index 7aaa7c56c15..00000000000
--- a/doc/user/project/milestones/img/milestone_group_create.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_new_group_milestone.png b/doc/user/project/milestones/img/milestones_new_group_milestone.png
new file mode 100644
index 00000000000..8780394d72e
--- /dev/null
+++ b/doc/user/project/milestones/img/milestones_new_group_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_new_project_milestone.png b/doc/user/project/milestones/img/milestones_new_project_milestone.png
new file mode 100644
index 00000000000..ba058428dfa
--- /dev/null
+++ b/doc/user/project/milestones/img/milestones_new_project_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_project_milestone_page.png b/doc/user/project/milestones/img/milestones_project_milestone_page.png
new file mode 100644
index 00000000000..9717075b8d0
--- /dev/null
+++ b/doc/user/project/milestones/img/milestones_project_milestone_page.png
Binary files differ
diff --git a/doc/user/project/milestones/img/milestones_promote_milestone.png b/doc/user/project/milestones/img/milestones_promote_milestone.png
new file mode 100644
index 00000000000..99bee1240d4
--- /dev/null
+++ b/doc/user/project/milestones/img/milestones_promote_milestone.png
Binary files differ
diff --git a/doc/user/project/milestones/img/sidebar.png b/doc/user/project/milestones/img/sidebar.png
deleted file mode 100644
index 274962a936c..00000000000
--- a/doc/user/project/milestones/img/sidebar.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index 27832b0fa2b..10e6321eb82 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -1,63 +1,111 @@
# Milestones
-Milestones allow you to organize issues and merge requests into a cohesive group,
-optionally setting a due date. A common use is keeping track of an upcoming
-software version. Milestones can be created per-project or per-group.
+## Overview
-## Creating a project milestone
+Milestones in GitLab are a way to track issues and merge requests created to achieve a broader goal in a certain period of time.
+
+Milestones allow you to organize issues and merge requests into a cohesive group, with an optional start date and an optional due date.
+
+## Project milestones and group milestones
+
+- **Project milestones** can be assigned to issues or merge requests in that project only.
+- **Group milestones** can be assigned to any issue or merge request of any project in that group.
+- In the [future](https://gitlab.com/gitlab-org/gitlab-ce/issues/36862), you will be able to assign group milestones to issues and merge reqeusts of projects in [subgroups](../../group/subgroups/index.md).
+
+## Creating milestones
+
+>**Note:**
+A permission level of `Developer` or higher is required to create milestones.
+
+### New project milestone
+
+To create a **project milestone**, navigate to **Issues > Milestones** in the project.
+
+Click the **New milestone** button. Enter the title, an optional description, an optional start date, and an optional due date. Click **Create milestone** to create the milestone.
+
+![New project milestone](img/milestones_new_project_milestone.png)
+
+### New group milestone
+
+To create a **group milestone**, follow similar steps from above to project milestones. Navigate to **Issues > Milestones** in the group and create it from there.
+
+![New group milestone](img/milestones_new_group_milestone.png)
+
+## Editing milestones
>**Note:**
-You need [Master permissions](../../permissions.md) in order to create a milestone.
+A permission level of `Developer` or higher is required to edit milestones.
+
+You can update a milestone by navigating to **Issues > Milestones** in the project or group and clicking the **Edit** button.
-You can find the milestones page under your project's **Issues âž” Milestones**.
-To create a new milestone, simply click the **New milestone** button when in the
-milestones page. A milestone can have a title, a description and start/due dates.
-Once you fill in all the details, hit the **Create milestone** button.
+You can delete a milestone by clicking the **Delete** button.
-![Creating a milestone](img/milestone_create.png)
+### Promoting project milestones to group milestones
-## Creating a group milestone
+If you are expanding from a few projects to a larger number of projects within the same group, you may want to share the same milestone among multiple projects in the same group. If you previously created a project milestone and now want to make it available for other milestones, you can promote it to a group milestone.
+
+From the project milestone list page, you can promote a project milestone to a group milestone. This will merge all project milestones across all projects in this group with the same name into a single group milestones. All issues and merge requests that previously were assigned one of these project milestones will now be assigned the new group milestones. This action cannot be reversed and the changes are permanent.
>**Note:**
-You need [Master permissions](../../permissions.md) in order to create a milestone.
+Not all features on the project milestone view are available on the group milestone view. If you promote a project milestone to a group milestone, you will lose these features. See [Milestone view](#milestone-view) to see which features are missing from the group milestone view.
+
+![Promote milestone](img/milestones_promote_milestone.png)
+
+## Assigning milestones from the sidebar
+
+Every issue and merge request can be assigned a milestone. The milestones are visible on every issue and merge request page, in the sidebar. They are also visible in the issue board. From the sidebar, you can assign or unassign a milestones to the object. You can also perform this as a [quick action](../quick_actions.md) in a comment. [As mentioned](#project-milestones-and-group-milestones), for a given issue or merge request, both project milestones and group milestones can be selected and assigned to the object.
+
+## Filtering issues and merge requests by milestone
+
+### Filtering in list pages
+
+From the project issue/merge request list pages and the group issue/merge request list pages, you can [filter](../../search/index.md#issues-and-merge-requests) by both group milestones and project milestones.
+
+### Filtering in issue boards
+
+From [project issue boards](../issue_board.md), you can filter by both group milestones and project milestones in the [search and filter bar](../../search/index.md#issue-boards).
+
+### Special milestone filters
+
+When filtering by milestone, in addition to choosing a specific project milestone or group milestone, you can choose a special milestone filter.
-You can create a milestone for a group that will be shared across group projects.
-On the group's **Issues âž” Milestones** page, you will be able to see the state
-of that milestone and the issues/merge requests count that it shares across the group projects. To create a new milestone click the **New milestone** button. The form is the same as when creating a milestone for a specific project which you can find in the previous item.
+- **No Milestone**: Show issues or merge requests with no 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.
-In addition to that you will be able to filter issues or merge requests by group milestones in all projects that belongs to the milestone group.
+## Milestone view
-## Milestone promotion
+Not all features in the project milestone view are available in the group milestone view. This table summarizes the differences:
-Project milestones can be promoted to group milestones if its project belongs to a group. When a milestone is promoted all other milestones across the group projects with the same title will be merged into it, which means all milestone's children like issues, merge requests and boards will be moved into the new promoted milestone.
-The promote button can be found in the milestone view or milestones list.
+| Feature | Project milestone view | Group milestone view |
+|---|:---:|:---:|
+| Title an description | ✓ | ✓ |
+| Issues assigned to milestone | ✓ | |
+| Merge requests assigned to milestone | ✓ | |
+| Participants and labels used | ✓ | |
+| Percentage complete | ✓ | ✓ |
+| Start date and due date | ✓ | ✓ |
+| Total issue time spent | ✓ | ✓ |
+| Total issue weight | ✓ | |
-## Special milestone filters
+The milestone view shows the title and description.
-In addition to the milestones that exist in the project or group, there are some
-special options available when filtering by milestone:
+### Project milestone features
-* **No Milestone** - only show issues or merge requests without a milestone.
-* **Upcoming** - show issues or merge request that belong to the next open
- milestone with a due date, by project. (For example: if project A has
- milestone v1 due in three days, and project B has milestone v2 due in a week,
- then this will show issues or merge requests from milestone v1 in project A
- and milestone v2 in project B.)
-* **Started** - show issues or merge requests from any milestone with a start
- date less than today. Note that this can return results from several
- milestones in the same project.
+These features are only available for project milestones and not group milestones.
-## Milestone sidebar
+- Issues assigned to the milestone are displayed in three columns: Unstarted issues, ongoing issues, and completed issues.
+- Merge requests assigned to the milestone are displayed in four columns: Work in progress merge requests, waiting for merge, rejected, and closed.
+- Participants and labels that are used in issues and merge requests that have the milestone assigned are displayed.
-The milestone sidebar shows percentage complete, start date and due date,
-issues, total issue weight, total issue time spent, and merge requests.
+### Milestone sidebar
-The percentage complete is calculated as: Closed and merged merge requests plus all closed issues divided by
-total merge requests and issues.
+The milestone sidebar on the milestone view shows the following:
-![Milestone sidebar](img/sidebar.png)
+- Percentage complete, which is calculated as number of closed issues plus number of closed/merged merge requests divided by total number issues and merge requests.
+- The start date and due date.
+- The total time spent on all issues that have the milestone assigned.
-## Quick actions
+For project milestones only, the milestone sidebar shows the total issue weight of all issues that have the milestone assigned.
-[Quick actions](../quick_actions.md) are available for assigning and removing
-project and group milestones.
+![Project milestone page](img/milestones_project_milestone_page.png)
diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md
index bd0cb437924..e4ee2f7cdfa 100644
--- a/doc/user/project/pages/getting_started_part_four.md
+++ b/doc/user/project/pages/getting_started_part_four.md
@@ -1,16 +1,13 @@
-# GitLab Pages from A to Z: Part 4
+---
+last_updated: 2018-02-16
+author: Marcia Ramos
+author_gitlab: marcia
+level: intermediate
+article_type: user guide
+date: 2017-02-22
+---
-> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide ||
-> **Level**: intermediate ||
-> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
-> **Publication date:** 2017/02/22
-
-- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
-- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
-- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
-- **Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages**
-
-## Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages
+# Creating and Tweaking GitLab CI/CD for GitLab Pages
[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves
numerous purposes, to build, test, and deploy your app
@@ -19,10 +16,13 @@ from GitLab through
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
+file called `.gitlab-ci.yml` placed at your website's root directory.
+
What this file actually does is telling the
[GitLab Runner](https://docs.gitlab.com/runner/) to run scripts
as you would do from the command line. The Runner acts as your
-terminal. GitLab CI tells the Runner which commands to run.
+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.
@@ -34,7 +34,7 @@ need to understand just a few things to be able to write our own
with its own syntax. You can always check your CI syntax with
the [GitLab CI Lint Tool](https://gitlab.com/ci/lint).
-**Practical Example:**
+## Practical example
Let's consider you have a [Jekyll](https://jekyllrb.com/) site.
To build it locally, you would open your terminal, and run `jekyll build`.
@@ -384,7 +384,3 @@ in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the
[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/).
-
-|||
-|:--|--:|
-|[**↠Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**](getting_started_part_three.md)||
diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md
index 1e19f422d94..290dfa5af84 100644
--- a/doc/user/project/pages/getting_started_part_one.md
+++ b/doc/user/project/pages/getting_started_part_one.md
@@ -1,31 +1,28 @@
-# GitLab Pages from A to Z: Part 1
-
-> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide ||
-> **Level**: beginner ||
-> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
-> **Publication date:** 2017/02/22
-
-- **Part 1: Static sites and GitLab Pages domains**
-- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
-- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
-- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
-
-## GitLab Pages from A to Z
-
-This is a comprehensive guide, made for those who want to
+---
+last_updated: 2018-02-16
+author: Marcia Ramos
+author_gitlab: marcia
+level: beginner
+article_type: user guide
+date: 2017-02-22
+---
+
+# Static sites and GitLab Pages domains
+
+This document is the beginning of a comprehensive guide, made for those who want to
publish a website with GitLab Pages but aren't familiar with
the entire process involved.
-This [first part](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of
+This [first document](#what-you-need-to-know-before-getting-started) of this series will present you to the concepts of
static sites, and go over how the default Pages domains work.
-The [second part](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy
+The [second document](getting_started_part_two.md) covers how to get started with GitLab Pages: deploy
a website from a forked project or create a new one from scratch.
-The [third part](getting_started_part_three.md) will show you how to set up a custom domain or subdomain
+The [third document](getting_started_part_three.md) will show you how to set up a custom domain or subdomain
to your site already deployed.
-The [fourth part](getting_started_part_four.md) will show you how to create and tweak GitLab CI for
+The [fourth document](getting_started_part_four.md) will show you how to create and tweak GitLab CI for
GitLab Pages.
To **enable** GitLab Pages for GitLab CE (Community Edition)
@@ -113,6 +110,4 @@ You can only create the highest level group website.
- On your GitLab instance, replace `gitlab.io` above with your
Pages server domain. Ask your sysadmin for this information.
-|||
-|:--|--:|
-||[**Part 2: Quick start guide - Setting up GitLab Pages →**](getting_started_part_two.md)|
+_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 0096f8507d2..430fe3af1f8 100644
--- a/doc/user/project/pages/getting_started_part_three.md
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -1,27 +1,19 @@
---
-last_updated: 2017-09-28
+last_updated: 2018-02-16
+author: Marcia Ramos
+author_gitlab: marcia
+level: beginner
+article_type: user guide
+date: 2017-02-22
---
-# GitLab Pages from A to Z: Part 3
+# GitLab Pages custom domains and SSL/TLS Certificates
-> **[Article Type](../../../development/writing_documentation.md#types-of-technical-articles)**: user guide ||
-> **Level**: beginner ||
-> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
-> **Publication date:** 2017-02-22 ||
-> **Last updated**: 2017-09-28
-
-- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
-- [Part 2: Quick start guide - Setting up GitLab Pages](getting_started_part_two.md)
-- **Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates**
-- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
-
-## Setting Up Custom Domains - DNS Records and SSL/TLS Certificates
-
-As described in the previous part of this series, setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages.
+Setting up GitLab Pages with custom domains, and adding SSL/TLS certificates to them, are optional features of GitLab Pages.
These steps assume you've already [set your site up](getting_started_part_two.md) and and it's served under the default Pages domain `namespace.gitlab.io`, or `namespace.gitlab.io/project-name`.
-### Adding your custom domain to GitLab Pages
+## Adding your custom domain to GitLab Pages
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:
@@ -36,7 +28,7 @@ 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.
-### DNS Records
+## DNS Records
A Domain Name System (DNS) web service routes visitors to websites
by translating domain names (such as `www.example.com`) into the
@@ -70,9 +62,9 @@ for the most popular hosting services:
- [Microsoft](https://msdn.microsoft.com/en-us/library/bb727018.aspx)
If your hosting service is not listed above, you can just try to
-search the web for "how to add dns record on <my hosting service>".
+search the web for `how to add dns record on <my hosting service>`.
-#### DNS A record
+### DNS A record
In case you want to point a root domain (`example.com`) to your
GitLab Pages site, deployed to `namespace.gitlab.io`, you need to
@@ -87,7 +79,7 @@ running on your instance).
![DNS A record pointing to GitLab.com Pages server](img/dns_add_new_a_record_example_updated.png)
-#### DNS CNAME record
+### DNS CNAME record
In case you want to point a subdomain (`hello-world.example.com`)
to your GitLab Pages site initially deployed to `namespace.gitlab.io`,
@@ -103,12 +95,32 @@ without any `/project-name`.
![DNS CNAME record pointing to GitLab.com project](img/dns_cname_record_example.png)
+#### DNS TXT record
+
+Unless your GitLab administrator has [disabled custom domain verification](../../../administration/pages/index.md#custom-domain-verification),
+you'll have to prove that you own the domain by creating a `TXT` record
+containing a verification code. The code will be displayed after you
+[add your custom domain to GitLab Pages settings](#add-your-custom-domain-to-gitlab-pages-settings).
+
+If using a [DNS A record](#dns-a-record), you can place the TXT record directly
+under the domain. If using a [DNS CNAME record](#dns-cname-record), the two record types won't
+co-exist, so you need to place the TXT record in a special subdomain of its own.
+
#### TL;DR
+If the domain has multiple uses (e.g., you host email on it as well):
+
| From | DNS Record | To |
| ---- | ---------- | -- |
| domain.com | A | 52.167.214.135 |
-| subdomain.domain.com | CNAME | namespace.gitlab.io |
+| domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
+
+If the domain is dedicated to GitLab Pages use and no other services run on it:
+
+| From | DNS Record | To |
+| ---- | ---------- | -- |
+| subdomain.domain.com | CNAME | gitlab.io |
+| _gitlab-pages-verification-code.subdomain.domain.com | TXT | gitlab-pages-verification-code=00112233445566778899aabbccddeeff |
> **Notes**:
>
@@ -119,7 +131,7 @@ domain. E.g., **do not** point your `subdomain.domain.com` to
`namespace.gitlab.io.` or `namespace.gitlab.io/`.
> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) from `104.208.235.32` to `52.167.214.135`.
-### Add your custom domain to GitLab Pages settings
+## 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
@@ -129,6 +141,17 @@ your site will be accessible only via HTTP:
![Add new domain](img/add_certificate_to_pages.png)
+Once you have added a new domain, you will need to **verify your ownership**
+(unless the GitLab administrator has disabled this feature). A verification code
+will be shown to you; add it as a [DNS TXT record](#dns-txt-record), then press
+the "Verify ownership" button to activate your new domain:
+
+![Verify your domain](img/verify_your_domain.png)
+
+Once your domain has been verified, leave the verification record in place -
+your domain will be periodically reverified, and may be disabled if the record
+is removed.
+
You can add more than one alias (custom domains and subdomains) to the same project.
An alias can be understood as having many doors leading to the same room.
@@ -136,13 +159,13 @@ All the aliases you've set to your site will be listed on **Setting > Pages**.
From that page, you can view, add, and remove them.
Note that [DNS propagation may take some time (up to 24h)](http://www.inmotionhosting.com/support/domain-names/dns-nameserver-changes/domain-names-dns-changes),
-although it's usually a matter of minutes to complete. Until it does, visit attempts
-to your domain will respond with a 404.
+although it's usually a matter of minutes to complete. Until it does, verification
+will fail and attempts to visit your domain will respond with a 404.
Read through the [general documentation on GitLab Pages](introduction.md#add-a-custom-domain-to-your-pages-website) to learn more about adding
custom domains to GitLab Pages sites.
-### SSL/TLS Certificates
+## SSL/TLS Certificates
Every GitLab Pages project on GitLab.com will be available under
HTTPS for the default Pages domain (`*.gitlab.io`). Once you set
@@ -155,16 +178,41 @@ Certificates are NOT required to add to your custom
(sub)domain on your GitLab Pages project, though they are
highly recommendable.
-The importance of having any website securely served under HTTPS
-is explained on the introductory section of the blog post
-[Secure GitLab Pages with StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/#https-a-quick-overview).
+Let's start with an introduction to the importance of HTTPS.
+Alternatively, jump ahead to [adding certificates to your project](#adding-certificates-to-your-project).
+
+### Why should I care about HTTPS?
+
+This might be your first question. If our sites are hosted by GitLab Pages,
+they are static, hence we are not dealing with server-side scripts
+nor credit card transactions, then why do we need secure connections?
-The reason why certificates are so important is that they encrypt
+Back in the 1990s, where HTTPS came out, [SSL](https://en.wikipedia.org/wiki/Transport_Layer_Security#SSL_1.0.2C_2.0_and_3.0) was considered a "special"
+security measure, necessary just for big companies, like banks and shoppings sites
+with financial transactions.
+Now we have a different picture. [According to Josh Aas](https://letsencrypt.org/2015/10/29/phishing-and-malware.html), Executive Director at [ISRG](https://en.wikipedia.org/wiki/Internet_Security_Research_Group):
+
+> _We’ve since come to realize that HTTPS is important for almost all websites. It’s important for any website that allows people to log in with a password, any website that [tracks its users](https://www.washingtonpost.com/news/the-switch/wp/2013/12/10/nsa-uses-google-cookies-to-pinpoint-targets-for-hacking/) in any way, any website that [doesn’t want its content altered](http://arstechnica.com/tech-policy/2014/09/why-comcasts-javascript-ad-injections-threaten-security-net-neutrality/), and for any site that offers content people might not want others to know they are consuming. We’ve also learned that any site not secured by HTTPS [can be used to attack other sites](http://krebsonsecurity.com/2015/04/dont-be-fodder-for-chinas-great-cannon/)._
+
+Therefore, the reason why certificates are so important is that they encrypt
the connection between the **client** (you, me, your visitors)
and the **server** (where you site lives), through a keychain of
authentications and validations.
-### Issuing Certificates
+How about taking Josh's advice and protecting our sites too? We will be
+well supported, and we'll contribute to a safer internet.
+
+### Organizations supporting HTTPS
+
+There is a huge movement in favor of securing all the web. W3C fully
+[supports the cause](https://w3ctag.github.io/web-https/) and explains very well
+the reasons for that. Richard Barnes, a writer for Mozilla Security Blog,
+suggested that [Firefox would deprecate HTTP](https://blog.mozilla.org/security/2015/04/30/deprecating-non-secure-http/),
+and would no longer accept unsecured connections. Recently, Mozilla published a
+[communication](https://blog.mozilla.org/security/2016/03/29/march-2016-ca-communication/)
+reiterating the importance of HTTPS.
+
+## Issuing Certificates
GitLab Pages accepts [PEM](https://support.quovadisglobal.com/kb/a37/what-is-pem-format.aspx) certificates issued by
[Certificate Authorities (CA)](https://en.wikipedia.org/wiki/Certificate_authority)
@@ -193,7 +241,7 @@ Their certs are valid up to 15 years. Read through the tutorial on
Regardless the CA you choose, the steps to add your certificate to
your Pages project are the same.
-#### What do you need
+### What do you need
1. A PEM certificate
1. An intermediate certificate
@@ -203,7 +251,7 @@ your Pages project are the same.
These fields are found under your **Project**'s **Settings** > **Pages** > **New Domain**.
-#### What's what?
+### What's what?
- A PEM certificate is the certificate generated by the CA,
which needs to be added to the field **Certificate (PEM)**.
@@ -216,7 +264,7 @@ are one of these cases.
- A public key is an encrypted key which validates
your PEM against your domain.
-#### Now what?
+### Now what?
Now that you hopefully understand why you need all
of this, it's simple:
@@ -233,6 +281,4 @@ just jumping a line between them.
regular text editors. Always use code editors (such as
Sublime Text, Atom, Dreamweaver, Brackets, etc).
-|||
-|:--|--:|
-|[**↠Part 2: Quick start guide - Setting up GitLab Pages**](getting_started_part_two.md)|[**Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages →**](getting_started_part_four.md)|
+_Read on about [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md)_
diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md
index 4a724dd5c1b..2274cac8ace 100644
--- a/doc/user/project/pages/getting_started_part_two.md
+++ b/doc/user/project/pages/getting_started_part_two.md
@@ -1,27 +1,23 @@
-# GitLab Pages from A to Z: Part 2
+---
+last_updated: 2018-02-16
+author: Marcia Ramos
+author_gitlab: marcia
+level: beginner
+article_type: user guide
+date: 2017-02-22
+---
-> **Article [Type](../../../development/writing_documentation.html#types-of-technical-articles)**: user guide ||
-> **Level**: beginner ||
-> **Author**: [Marcia Ramos](https://gitlab.com/marcia) ||
-> **Publication date:** 2017/02/22
-
-- [Part 1: Static sites and GitLab Pages domains](getting_started_part_one.md)
-- **Part 2: Quick start guide - Setting up GitLab Pages**
-- [Part 3: Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md)
-- [Part 4: Creating and tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
-
-## Setting up GitLab Pages
-
-For a complete step-by-step tutorial, please read the
-blog post [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/). The following sections will explain
-what do you need and why do you need them.
+# Projects for GitLab Pages and URL structure
## What you need to get started
+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
+1. A `public` directory with the content of the website
Optional Features:
@@ -51,35 +47,26 @@ containing the most popular SSGs templates.
Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've
created for the steps below.
-1. Choose your SSG template
-1. Fork a project from the [Pages group](https://gitlab.com/pages)
-1. Remove the fork relationship by navigating to your **Project**'s **Settings** > **Edit Project**
+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 relashionship**:
![remove fork relashionship](img/remove_fork_relashionship.png)
-1. Enable Shared Runners for your fork: navigate to your **Project**'s **Settings** > **Pipelines**
-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**
-
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** > **Edit Project** > **Rename repository**
+- 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) to 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 likelly, it will be in the SSG's config file.
> **Notes:**
>
->1. Why do I need to remove the fork relationship?
+> Why do I need to remove the fork relationship?
>
-> Unless you want to contribute to the original project,
+> 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.
->
-> 2. Why do I need to enable Shared Runners?
->
-> Shared Runners will run the script set by your GitLab CI/CD
-configuration file. They're enabled by default to new projects,
-but not to forks.
### Create a project from scratch
@@ -108,7 +95,7 @@ where you'll find its default URL.
> - 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`. Do do that, please
-read through the article [Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
+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)
@@ -121,7 +108,7 @@ 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
+## URLs and Baseurls
Every Static Site Generator (SSG) default configuration expects
to find your website under a (sub)domain (`example.com`), not
@@ -149,11 +136,7 @@ example we've just mentioned, you'd have to change Jekyll's `_config.yml` to:
baseurl: ""
```
-### Custom Domains
+## Custom Domains
-GitLab Pages supports custom domains and subdomains, served under HTTPS or HTTPS.
+GitLab Pages supports custom domains and subdomains, served under HTTP or HTTPS.
Please check the [next part](getting_started_part_three.md) of this series for an overview.
-
-|||
-|:--|--:|
-|[**↠Part 1: Static sites, domains, DNS records, and SSL/TLS certificates**](getting_started_part_one.md)|[**Setting Up Custom Domains - DNS Records and SSL/TLS Certificates →**](getting_started_part_three.md)|
diff --git a/doc/user/project/pages/img/verify_your_domain.png b/doc/user/project/pages/img/verify_your_domain.png
new file mode 100644
index 00000000000..89c69cac9a5
--- /dev/null
+++ b/doc/user/project/pages/img/verify_your_domain.png
Binary files differ
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index 8404d789de6..a65aa758198 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -37,10 +37,10 @@ to secure them.
Read the following tutorials to know more about:
-- [Static websites and GitLab Pages domains](getting_started_part_one.md)
-- [Forking projects and creating new ones from scratch, URLs and baseurls](getting_started_part_two.md)
-- [Custom domains and subdomains, DNS records, SSL/TLS certificates](getting_started_part_three.md)
-- [How to create your own `.gitlab-ci.yml` for your site](getting_started_part_four.md)
+- [Static websites and GitLab Pages domains](getting_started_part_one.md): Understand what is a static website, and how GitLab Pages default domains work
+- [Projects for GitLab Pages and URL structure](getting_started_part_two.md): Forking projects and creating new ones from scratch, understanding URLs structure and baseurls
+- [GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md): How to add custom domains and subdomains to your website, configure DNS records, and SSL/TLS certificates
+- [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md): Understand how to create your own `.gitlab-ci.yml` for your site
- [Technical aspects, custom 404 pages, limitations](introduction.md)
- [Hosting on GitLab.com with GitLab Pages](https://about.gitlab.com/2016/04/07/gitlab-pages-setup/) (outdated)
@@ -54,7 +54,6 @@ _Blog posts for securing GitLab Pages custom domains with SSL/TLS certificates:_
- [CloudFlare](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
- [Let's Encrypt](https://about.gitlab.com/2016/04/11/tutorial-securing-your-gitlab-pages-with-tls-and-letsencrypt/) (outdated)
-- [StartSSL](https://about.gitlab.com/2016/06/24/secure-gitlab-pages-with-startssl/) (deprecated)
## Advanced use
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index ce081cedd71..e6aede7f46e 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -18,7 +18,7 @@ documentation.
> **Important:**
For security reasons, when using the command line, we strongly recommend
-you to [connect with GitLab via SSH](../../../ssh/README.md).
+that you [connect with GitLab via SSH](../../../ssh/README.md).
## Files
@@ -66,8 +66,7 @@ your implementation with your team.
You can live preview changes submitted to a new branch with
[Review Apps](../../../ci/review_apps/index.md).
-With [GitLab Enterprise Edition](https://about.gitlab.com/products/)
-subscriptions, you can also request
+With [GitLab Starter](https://about.gitlab.com/products/), you can also request
[approval](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html) from your managers.
To create, delete, and [branches](branches/index.md) via GitLab's UI:
diff --git a/doc/user/project/settings/import_export.md b/doc/user/project/settings/import_export.md
index b8f865679a2..dedf102fc37 100644
--- a/doc/user/project/settings/import_export.md
+++ b/doc/user/project/settings/import_export.md
@@ -22,6 +22,7 @@
> in the import side is required to map the users, based on email or username.
> Otherwise, a supplementary comment is left to mention the original author and
> the MRs, notes or issues will be owned by the importer.
+> - Control project Import/Export with the [API](../../../api/project_import_export.md).
Existing projects running on any GitLab instance or GitLab.com can be exported
with all their related data and be moved into a new GitLab instance.
diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
index 8fff3d591fe..377eee69c11 100644
--- a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
+++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
@@ -34,7 +34,7 @@ Documentation for GitLab instance administrators is under [LFS administration do
credentials store is recommended
* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have
to add the URL to Git config manually (see [troubleshooting](#troubleshooting))
-
+
>**Note**: With 8.12 GitLab added LFS support to SSH. The Git LFS communication
still goes over HTTP, but now the SSH client passes the correct credentials
to the Git LFS client, so no action is required by the user.
@@ -85,6 +85,8 @@ git lfs fetch master
## File Locking
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/35856) in GitLab 10.5.
+
The first thing to do before using File Locking is to tell Git LFS which
kind of files are lockable. The following command will store PNG files
in LFS and flag them as lockable:
diff --git a/features/group/members.feature b/features/group/members.feature
deleted file mode 100644
index 49a44f57cbb..00000000000
--- a/features/group/members.feature
+++ /dev/null
@@ -1,12 +0,0 @@
-Feature: Group Members
- Background:
- Given I sign in as "John Doe"
- And "John Doe" is owner of group "Owned"
- And "John Doe" is guest of group "Guest"
-
- Scenario: Search member by name
- Given "Mary Jane" is guest of group "Guest"
- And I visit group "Guest" members page
- When I search for 'Mary' member
- Then I should see user "Mary Jane" in team list
- Then I should not see user "John Doe" in team list
diff --git a/features/group/milestones.feature b/features/group/milestones.feature
deleted file mode 100644
index 2211acfee20..00000000000
--- a/features/group/milestones.feature
+++ /dev/null
@@ -1,48 +0,0 @@
-Feature: Group Milestones
- Background:
- Given I sign in as "John Doe"
- And "John Doe" is owner of group "Owned"
-
- Scenario: I should see group "Owned" milestone index page with no milestones
- When I visit group "Owned" page
- And I click on group milestones
- Then I should see group milestones index page has no milestones
-
- Scenario: I should see group "Owned" milestone index page with milestones
- Given Group has projects with milestones
- When I visit group "Owned" page
- And I click on group milestones
- Then I should see group milestones index page with milestones
-
- Scenario: I should see group "Owned" milestone show page
- Given Group has projects with milestones
- When I visit group "Owned" page
- And I click on group milestones
- And I click on one group milestone
- Then I should see group milestone with descriptions and expiry date
- And I should see group milestone with all issues and MRs assigned to that milestone
-
- Scenario: Create group milestones
- Given I visit group "Owned" milestones page
- And I click new milestone button
- And I fill milestone name
- When I press create mileston button
- Then group milestone should be created
-
- Scenario: I should see Issues listed with labels
- Given Group has projects with milestones
- When I visit group "Owned" page
- And I click on group milestones
- And I click on one group milestone
- Then I should see the "bug" label
- And I should see the "feature" label
- And I should see the project name in the Issue row
-
- @javascript
- Scenario: I should see the Labels tab
- Given Group has projects with milestones
- When I visit group "Owned" page
- And I click on group milestones
- And I click on one group milestone
- And I click on the "Labels" tab
- Then I should see the list of labels
diff --git a/features/profile/profile.feature b/features/profile/profile.feature
deleted file mode 100644
index 3263d3e212b..00000000000
--- a/features/profile/profile.feature
+++ /dev/null
@@ -1,85 +0,0 @@
-@profile
-Feature: Profile
- Background:
- Given I sign in as a user
-
- Scenario: I look at my profile
- Given I visit profile page
- Then I should see my profile info
-
- @javascript
- Scenario: I can see groups I belong to
- Given I have group with projects
- When I visit profile page
- And I click on my profile picture
- Then I should see my user page
- And I should see groups I belong to
-
- Scenario: I edit profile
- Given I visit profile page
- Then I change my profile info
- And I should see new profile info
-
- Scenario: I change my password without old one
- Given I visit profile password page
- When I try change my password w/o old one
- Then I should see a missing password error message
- And I should be redirected to password page
-
- Scenario: I change my password
- Given I visit profile password page
- Then I change my password
- And I should be redirected to sign in page
-
- Scenario: I edit my avatar
- Given I visit profile page
- Then I change my avatar
- And I should see new avatar
- And I should see the "Remove avatar" button
- And I should see the gravatar host link
-
- Scenario: I remove my avatar
- Given I visit profile page
- And I have an avatar
- When I remove my avatar
- Then I should see my gravatar
- And I should not see the "Remove avatar" button
- And I should see the gravatar host link
-
- Scenario: My password is expired
- Given my password is expired
- And I am not an ldap user
- Given I visit profile password page
- Then I redirected to expired password page
- And I submit new password
- And I redirected to sign in page
-
- Scenario: I unsuccessfully change my password
- Given I visit profile password page
- When I unsuccessfully change my password
- Then I should see a password error message
-
- Scenario: I visit history tab
- Given I logout
- And I sign in via the UI
- And I have activity
- When I visit Authentication log page
- Then I should see my activity
-
- Scenario: I visit my user page
- When I visit profile page
- And I click on my profile picture
- Then I should see my user page
-
- Scenario: I can manage application
- Given I visit profile applications page
- Then I should see application form
- Then I fill application form out and submit
- And I see application
- Then I click edit
- And I see edit application form
- Then I change name of application and submit
- And I see that application was changed
- Then I visit profile applications page
- And I click to remove application
- Then I see that application is removed
diff --git a/features/project/network_graph.feature b/features/project/network_graph.feature
deleted file mode 100644
index 93c884e23c5..00000000000
--- a/features/project/network_graph.feature
+++ /dev/null
@@ -1,46 +0,0 @@
-Feature: Project Network Graph
- Background:
- Given I sign in as a user
- And I own project "Shop"
- And I visit project "Shop" network page
-
- @javascript
- Scenario: I should see project network
- Then page should have network graph
- And page should select "master" in select box
- And page should have "master" on graph
-
- @javascript
- Scenario: I should see project network with 'test' branch
- When I visit project network page on branch 'test'
- Then page should have 'test' on graph
-
- @javascript
- Scenario: I should switch "branch" and "tag"
- When I switch ref to "feature"
- Then page should select "feature" in select box
- And page should have "feature" on graph
- When I switch ref to "v1.0.0"
- Then page should select "v1.0.0" in select box
- And page should have "v1.0.0" on graph
-
- @javascript
- Scenario: I should looking for a commit by SHA
- When I looking for a commit by SHA of "v1.0.0"
- Then page should have network graph
- And page should select "master" in select box
- And page should have "v1.0.0" on graph
-
- @javascript
- Scenario: I should filter selected tag
- When I switch ref to "v1.0.0"
- Then page should have "v1.0.0" in title
- Then page should have content not containing "v1.0.0"
- When click "Show only selected branch" checkbox
- Then page should only have content from "v1.0.0"
- When click "Show only selected branch" checkbox
- Then page should have content not containing "v1.0.0"
-
- Scenario: I should fail to look for a commit
- When I look for a commit by ";"
- Then I should see non-existent git revision error message
diff --git a/features/project/pages.feature b/features/project/pages.feature
deleted file mode 100644
index 56e47287b5c..00000000000
--- a/features/project/pages.feature
+++ /dev/null
@@ -1,87 +0,0 @@
-Feature: Project Pages
- Background:
- Given I sign in as a user
- And I own a project
-
- Scenario: I cannot navigate to Pages settings if pages enabled
- Given pages are disabled
- And I visit my project's settings page
- Then I should not see the "Pages" tab
-
- Scenario: I can navigate to Pages settings if pages enabled
- Given pages are enabled
- And I visit my project's settings page
- Then I should see the "Pages" tab
-
- Scenario: I can see the pages usage if not deployed
- Given pages are enabled
- When I visit the Project Pages
- Then I should see the usage of GitLab Pages
-
- Scenario: I can access the pages if deployed
- Given pages are enabled
- And pages are deployed
- When I visit the Project Pages
- Then I should be able to access the Pages
-
- Scenario: I should message that domains support is disabled
- Given pages are enabled
- And pages are deployed
- And support for external domains is disabled
- When I visit the Project Pages
- Then I should see that support for domains is disabled
-
- Scenario: I should see a new domain button
- Given pages are enabled
- And pages are exposed on external HTTP address
- When I visit the Project Pages
- And I should be able to add a New Domain
-
- Scenario: I should be able to add a new domain
- Given pages are enabled
- And pages are exposed on external HTTP address
- When I visit add a new Pages Domain
- And I fill the domain
- And I click on "Create New Domain"
- Then I should see a new domain added
-
- Scenario: I should be able to add a new domain for project in group namespace
- Given I own a project in some group namespace
- And pages are enabled
- And pages are exposed on external HTTP address
- When I visit add a new Pages Domain
- And I fill the domain
- And I click on "Create New Domain"
- Then I should see a new domain added
-
- Scenario: I should be denied to add the same domain twice
- Given pages are enabled
- And pages are exposed on external HTTP address
- And pages domain is added
- When I visit add a new Pages Domain
- And I fill the domain
- And I click on "Create New Domain"
- Then I should see error message that domain already exists
-
- Scenario: I should message that certificates support is disabled when trying to add a new domain
- Given pages are enabled
- And pages are exposed on external HTTP address
- And pages domain is added
- When I visit add a new Pages Domain
- Then I should see that support for certificates is disabled
-
- Scenario: I should be able to add a new domain with certificate
- Given pages are enabled
- And pages are exposed on external HTTPS address
- When I visit add a new Pages Domain
- And I fill the domain
- And I fill the certificate and key
- And I click on "Create New Domain"
- Then I should see a new domain added
-
- Scenario: I can remove the pages if deployed
- Given pages are enabled
- And pages are deployed
- When I visit the Project Pages
- And I click Remove Pages
- Then The Pages should get removed
diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb
index 0ab1012660c..97bcca7730b 100644
--- a/features/steps/group/members.rb
+++ b/features/steps/group/members.rb
@@ -9,14 +9,6 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
expect(group_members_list).to have_content("John Doe")
end
- step 'I should not see user "John Doe" in team list' do
- expect(group_members_list).not_to have_content("John Doe")
- end
-
- step 'I should see user "Mary Jane" in team list' do
- expect(group_members_list).to have_content("Mary Jane")
- end
-
step 'I should not see user "Mary Jane" in team list' do
expect(group_members_list).not_to have_content("Mary Jane")
end
@@ -41,13 +33,6 @@ class Spinach::Features::GroupMembers < Spinach::FeatureSteps
# poltergeist always confirms popups.
end
- step 'I search for \'Mary\' member' do
- page.within '.member-search-form' do
- fill_in 'search', with: 'Mary'
- find('.member-search-btn').click
- end
- end
-
step 'I change the "Mary Jane" role to "Developer"' do
member = mary_jane_member
diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb
deleted file mode 100644
index 818bbb50d0e..00000000000
--- a/features/steps/group/milestones.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
- include WaitForRequests
- include SharedAuthentication
- include SharedPaths
- include SharedGroup
- include SharedUser
-
- step 'I click on group milestones' do
- visit group_milestones_path('owned')
- end
-
- step 'I should see group milestones index page has no milestones' do
- expect(page).to have_content('No milestones to show')
- end
-
- step 'Group has projects with milestones' do
- group_milestone
- end
-
- step 'I should see group milestones index page with milestones' do
- expect(page).to have_content('Version 7.2')
- expect(page).to have_content('GL-113')
- expect(page).to have_link('3 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2"))
- expect(page).to have_link('0 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113"))
- end
-
- step 'I click on one group milestone' do
- milestones = Milestone.where(title: 'GL-113')
- @global_milestone = GlobalMilestone.new('GL-113', milestones)
-
- click_link 'GL-113'
- end
-
- step 'I should see group milestone with descriptions and expiry date' do
- expect(page).to have_content('expires on Aug 20, 2114')
- end
-
- step 'I should see group milestone with all issues and MRs assigned to that milestone' do
- expect(page).to have_content('Milestone GL-113')
- expect(page).to have_content('Issues 3 Open: 3 Closed: 0')
- issue = Milestone.find_by(name: 'GL-113').issues.first
- expect(page).to have_link(issue.title, href: project_issue_path(issue.project, issue))
- end
-
- step 'I fill milestone name' do
- fill_in 'milestone_title', with: 'v2.9.0'
- end
-
- step 'I click new milestone button' do
- page.within('.nav-controls') do
- click_link "New milestone"
- end
- end
-
- step 'I press create mileston button' do
- click_button "Create milestone"
- end
-
- step 'group milestone should be created' do
- group = Group.find_by(name: 'Owned')
- expect(page).to have_content group.milestones.find_by_title('v2.9.0').title
- end
-
- step 'I should see the "bug" label' do
- page.within('#tab-issues') do
- expect(page).to have_content 'bug'
- end
- end
-
- step 'I should see the "feature" label' do
- page.within('#tab-issues') do
- expect(page).to have_content 'bug'
- end
- end
-
- step 'I should see the project name in the Issue row' do
- page.within('#tab-issues') do
- @global_milestone.projects.each do |project|
- expect(page).to have_content project.name
- end
- end
- end
-
- step 'I click on the "Labels" tab' do
- page.within('.content .nav-links') do
- page.find(:xpath, "//a[@href='#tab-labels']").click
- end
- end
-
- step 'I should see the list of labels' do
- wait_for_requests
-
- page.within('#tab-labels') do
- expect(page).to have_content 'bug'
- expect(page).to have_content 'feature'
- end
- end
-
- private
-
- def group_milestone
- group = owned_group
-
- %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path|
- project = create(:project, path: path, group: group)
- milestone = create :milestone, title: "Version 7.2", project: project
-
- create(:label, project: project, title: 'bug')
- create(:label, project: project, title: 'feature')
-
- create :issue,
- project: project,
- assignees: [current_user],
- author: current_user,
- milestone: milestone
-
- milestone = create :milestone,
- title: "GL-113",
- project: project,
- due_date: '2114-08-20',
- description: 'Lorem Ipsum is simply dummy text'
-
- issue = create :issue,
- project: project,
- assignees: [current_user],
- author: current_user,
- milestone: milestone
-
- issue.labels << project.labels.find_by(title: 'bug')
- issue.labels << project.labels.find_by(title: 'feature')
- end
-
- current_user.refresh_authorized_projects
- end
-end
diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb
deleted file mode 100644
index d3b88ae8d2a..00000000000
--- a/features/steps/profile/profile.rb
+++ /dev/null
@@ -1,226 +0,0 @@
-class Spinach::Features::Profile < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
-
- step 'I should see my profile info' do
- expect(page).to have_content "This information will appear on your profile"
- end
-
- step 'I change my profile info' do
- fill_in 'user_skype', with: 'testskype'
- fill_in 'user_linkedin', with: 'testlinkedin'
- fill_in 'user_twitter', with: 'testtwitter'
- fill_in 'user_website_url', with: 'testurl'
- fill_in 'user_location', with: 'Ukraine'
- fill_in 'user_bio', with: 'I <3 GitLab'
- fill_in 'user_organization', with: 'GitLab'
- click_button 'Update profile settings'
- @user.reload
- end
-
- step 'I should see new profile info' do
- expect(@user.skype).to eq 'testskype'
- expect(@user.linkedin).to eq 'testlinkedin'
- expect(@user.twitter).to eq 'testtwitter'
- expect(@user.website_url).to eq 'testurl'
- expect(@user.bio).to eq 'I <3 GitLab'
- expect(@user.organization).to eq 'GitLab'
- expect(find('#user_location').value).to eq 'Ukraine'
- end
-
- step 'I change my avatar' do
- attach_file(:user_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif'))
- click_button "Update profile settings"
- @user.reload
- end
-
- step 'I should see new avatar' do
- expect(@user.avatar).to be_instance_of AvatarUploader
- expect(@user.avatar.url).to eq "/uploads/-/system/user/avatar/#{@user.id}/banana_sample.gif"
- end
-
- step 'I should see the "Remove avatar" button' do
- expect(page).to have_link("Remove avatar")
- end
-
- step 'I have an avatar' do
- attach_file(:user_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif'))
- click_button "Update profile settings"
- @user.reload
- end
-
- step 'I remove my avatar' do
- click_link "Remove avatar"
- @user.reload
- end
-
- step 'I should see my gravatar' do
- expect(@user.avatar?).to eq false
- end
-
- step 'I should not see the "Remove avatar" button' do
- expect(page).not_to have_link("Remove avatar")
- end
-
- step 'I should see the gravatar host link' do
- expect(page).to have_link("gravatar.com")
- end
-
- step 'I try change my password w/o old one' do
- page.within '.update-password' do
- fill_in "user_password", with: "22233344"
- fill_in "user_password_confirmation", with: "22233344"
- click_button "Save password"
- end
- end
-
- step 'I change my password' do
- page.within '.update-password' do
- fill_in "user_current_password", with: "12345678"
- fill_in "user_password", with: "22233344"
- fill_in "user_password_confirmation", with: "22233344"
- click_button "Save password"
- end
- end
-
- step 'I unsuccessfully change my password' do
- page.within '.update-password' do
- fill_in "user_current_password", with: "12345678"
- fill_in "user_password", with: "password"
- fill_in "user_password_confirmation", with: "confirmation"
- click_button "Save password"
- end
- end
-
- step "I should see a missing password error message" do
- page.within ".flash-container" do
- expect(page).to have_content "You must provide a valid current password"
- end
- end
-
- step "I should see a password error message" do
- page.within '.alert-danger' do
- expect(page).to have_content "Password confirmation doesn't match"
- end
- end
-
- step 'I have activity' do
- create(:closed_issue_event, author: current_user)
- end
-
- step 'I should see my activity' do
- expect(page).to have_content "Signed in with standard authentication"
- end
-
- step 'my password is expired' do
- current_user.update_attributes(password_expires_at: Time.now - 1.hour)
- end
-
- step "I am not an ldap user" do
- current_user.identities.delete
- expect(current_user.ldap_user?).to eq false
- end
-
- step 'I redirected to expired password page' do
- expect(current_path).to eq new_profile_password_path
- end
-
- step 'I submit new password' do
- fill_in :user_current_password, with: '12345678'
- fill_in :user_password, with: '12345678'
- fill_in :user_password_confirmation, with: '12345678'
- click_button "Set new password"
- end
-
- step 'I redirected to sign in page' do
- expect(current_path).to eq new_user_session_path
- end
-
- step 'I should be redirected to password page' do
- expect(current_path).to eq edit_profile_password_path
- end
-
- step 'I should be redirected to account page' do
- expect(current_path).to eq profile_account_path
- end
-
- step 'I click on my profile picture' do
- find(:css, '.header-user-dropdown-toggle').click
-
- page.within ".header-user" do
- click_link "Profile"
- end
- end
-
- step 'I should see my user page' do
- page.within ".cover-block" do
- expect(page).to have_content current_user.name
- expect(page).to have_content current_user.username
- end
- end
-
- step 'I have group with projects' do
- @group = create(:group)
- @group.add_owner(current_user)
- @project = create(:project, :repository, namespace: @group)
- @event = create(:closed_issue_event, project: @project)
-
- @project.add_master(current_user)
- end
-
- step 'I should see groups I belong to' do
- page.within ".content" do
- click_link "Groups"
- end
-
- page.within "#groups" do
- expect(page).to have_content @group.name
- end
- end
-
- step 'I should see application form' do
- expect(page).to have_content "Add new application"
- end
-
- step 'I fill application form out and submit' do
- fill_in :doorkeeper_application_name, with: 'test'
- fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com'
- click_on "Save application"
- end
-
- step 'I see application' do
- expect(page).to have_content "Application: test"
- expect(page).to have_content "Application Id"
- expect(page).to have_content "Secret"
- end
-
- step 'I click edit' do
- click_on "Edit"
- end
-
- step 'I see edit application form' do
- expect(page).to have_content "Edit application"
- end
-
- step 'I change name of application and submit' do
- expect(page).to have_content "Edit application"
- fill_in :doorkeeper_application_name, with: 'test_changed'
- click_on "Save application"
- end
-
- step 'I see that application was changed' do
- expect(page).to have_content "test_changed"
- expect(page).to have_content "Application Id"
- expect(page).to have_content "Secret"
- end
-
- step 'I click to remove application' do
- page.within '.oauth-applications' do
- click_on "Destroy"
- end
- end
-
- step "I see that application is removed" do
- expect(page.find(".oauth-applications")).not_to have_content "test_changed"
- end
-end
diff --git a/features/steps/project/network_graph.rb b/features/steps/project/network_graph.rb
deleted file mode 100644
index ba98d861e7b..00000000000
--- a/features/steps/project/network_graph.rb
+++ /dev/null
@@ -1,116 +0,0 @@
-class Spinach::Features::ProjectNetworkGraph < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedProject
-
- step 'page should have network graph' do
- expect(page).to have_selector ".network-graph"
- end
-
- When 'I visit project "Shop" network page' do
- # Stub Graph max_size to speed up test (10 commits vs. 650)
- Network::Graph.stub(max_count: 10)
-
- @project = Project.find_by(name: "Shop")
- visit project_network_path(@project, "master")
- end
-
- step "I visit project network page on branch 'test'" do
- visit project_network_path(@project, "'test'")
- end
-
- step 'page should select "master" in select box' do
- expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
- end
-
- step 'page should select "v1.0.0" in select box' do
- expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0"
- end
-
- step 'page should have "master" on graph' do
- page.within '.network-graph' do
- expect(page).to have_content 'master'
- end
- end
-
- step "page should have 'test' on graph" do
- page.within '.network-graph' do
- expect(page).to have_content "'test'"
- end
- end
-
- When 'I switch ref to "feature"' do
- first('.js-project-refs-dropdown').click
-
- page.within '.project-refs-form' do
- click_link 'feature'
- end
- end
-
- When 'I switch ref to "v1.0.0"' do
- first('.js-project-refs-dropdown').click
-
- page.within '.project-refs-form' do
- click_link 'v1.0.0'
- end
- end
-
- When 'click "Show only selected branch" checkbox' do
- find('#filter_ref').click
- end
-
- step 'page should have content not containing "v1.0.0"' do
- page.within '.network-graph' do
- expect(page).to have_content 'Change some files'
- end
- end
-
- step 'page should have "v1.0.0" in title' do
- expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false
- end
-
- step 'page should only have content from "v1.0.0"' do
- page.within '.network-graph' do
- expect(page).not_to have_content 'Change some files'
- end
- end
-
- step 'page should select "feature" in select box' do
- expect(page).to have_selector '.dropdown-menu-toggle', text: "feature"
- end
-
- step 'page should select "v1.0.0" in select box' do
- expect(page).to have_selector '.dropdown-menu-toggle', text: "v1.0.0"
- end
-
- step 'page should have "feature" on graph' do
- page.within '.network-graph' do
- expect(page).to have_content 'feature'
- end
- end
-
- When 'I looking for a commit by SHA of "v1.0.0"' do
- page.within ".network-form" do
- fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
- find('button').click
- end
- sleep 2
- end
-
- step 'page should have "v1.0.0" on graph' do
- page.within '.network-graph' do
- expect(page).to have_content 'v1.0.0'
- end
- end
-
- When 'I look for a commit by ";"' do
- page.within ".network-form" do
- fill_in 'extended_sha1', with: ';'
- find('button').click
- end
- end
-
- step 'I should see non-existent git revision error message' do
- expect(page).to have_selector '.flash-alert', text: "Git revision ';' does not exist."
- end
-end
diff --git a/features/steps/project/pages.rb b/features/steps/project/pages.rb
deleted file mode 100644
index f03630e5a91..00000000000
--- a/features/steps/project/pages.rb
+++ /dev/null
@@ -1,152 +0,0 @@
-class Spinach::Features::ProjectPages < Spinach::FeatureSteps
- include SharedAuthentication
- include SharedPaths
- include SharedProject
-
- step 'pages are enabled' do
- allow(Gitlab.config.pages).to receive(:enabled).and_return(true)
- allow(Gitlab.config.pages).to receive(:host).and_return('example.com')
- allow(Gitlab.config.pages).to receive(:port).and_return(80)
- allow(Gitlab.config.pages).to receive(:https).and_return(false)
- end
-
- step 'pages are disabled' do
- allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
- end
-
- step 'I visit the Project Pages' do
- visit project_pages_path(@project)
- end
-
- step 'I should see the usage of GitLab Pages' do
- expect(page).to have_content('Configure pages')
- end
-
- step 'I should see the "Pages" tab' do
- page.within '.nav-sidebar' do
- expect(page).to have_link('Pages')
- end
- end
-
- step 'I should not see the "Pages" tab' do
- page.within '.nav-sidebar' do
- expect(page).not_to have_link('Pages')
- end
- end
-
- step 'pages are deployed' do
- pipeline = @project.pipelines.create(ref: 'HEAD',
- sha: @project.commit('HEAD').sha,
- source: :push,
- protected: false)
-
- build = build(:ci_build,
- project: @project,
- pipeline: pipeline,
- ref: 'HEAD',
- legacy_artifacts_file: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip'),
- legacy_artifacts_metadata: fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta')
- )
-
- result = ::Projects::UpdatePagesService.new(@project, build).execute
- expect(result[:status]).to eq(:success)
- end
-
- step 'I should be able to access the Pages' do
- expect(page).to have_content('Access pages')
- end
-
- step 'I should see that support for domains is disabled' do
- expect(page).to have_content('Support for domains and certificates is disabled')
- end
-
- step 'support for external domains is disabled' do
- allow(Gitlab.config.pages).to receive(:external_http).and_return(nil)
- allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
- end
-
- step 'pages are exposed on external HTTP address' do
- allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80'])
- allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
- end
-
- step 'pages are exposed on external HTTPS address' do
- allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80'])
- allow(Gitlab.config.pages).to receive(:external_https).and_return(['1.1.1.1:443'])
- end
-
- step 'I should be able to add a New Domain' do
- expect(page).to have_content('New Domain')
- end
-
- step 'I visit add a new Pages Domain' do
- visit new_project_pages_domain_path(@project)
- end
-
- step 'I fill the domain' do
- fill_in 'Domain', with: 'my.test.domain.com'
- end
-
- step 'I click on "Create New Domain"' do
- click_button 'Create New Domain'
- end
-
- step 'I should see a new domain added' do
- expect(page).to have_content('Domains (1)')
- expect(page).to have_content('my.test.domain.com')
- end
-
- step 'pages domain is added' do
- @project.pages_domains.create!(domain: 'my.test.domain.com')
- end
-
- step 'I should see error message that domain already exists' do
- expect(page).to have_content('Domain has already been taken')
- end
-
- step 'I should see that support for certificates is disabled' do
- expect(page).to have_content('Support for custom certificates is disabled')
- end
-
- step 'I fill the certificate and key' do
- fill_in 'Certificate (PEM)', with: '-----BEGIN CERTIFICATE-----
-MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0
-LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ
-MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
-gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa
-SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT
-nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w
-DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD
-VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh
-IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ
-joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese
-5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg
-YHi2yesCrOvVXt+lgPTd
------END CERTIFICATE-----'
-
- fill_in 'Key (PEM)', with: '-----BEGIN PRIVATE KEY-----
-MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN
-SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t
-PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB
-kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd
-j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/
-uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR
-5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O
-AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K
-EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh
-Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C
-m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH
-EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx
-63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi
-nNp/xedE1YxutQ==
------END PRIVATE KEY-----'
- end
-
- step 'I click Remove Pages' do
- click_link 'Remove pages'
- end
-
- step 'The Pages should get removed' do
- expect(@project.pages_deployed?).to be_falsey
- end
-end
diff --git a/features/support/env.rb b/features/support/env.rb
index 7f5b4c1c11b..15211995918 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -20,15 +20,16 @@ Dir["#{Rails.root}/features/steps/shared/*.rb"].each { |file| require file }
Spinach.hooks.before_run do
include RSpec::Mocks::ExampleMethods
+ include ActiveJob::TestHelper
+ include FactoryBot::Syntax::Methods
+ include GitlabRoutingHelper
+
RSpec::Mocks.setup
TestEnv.init(mailer: false)
# skip pre-receive hook check so we can use
# web editor and merge
TestEnv.disable_pre_receive
-
- include FactoryBot::Syntax::Methods
- include GitlabRoutingHelper
end
Spinach.hooks.after_scenario do |scenario_data, step_definitions|
diff --git a/lib/api/api.rb b/lib/api/api.rb
index e953f3d2eca..754549f72f0 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -138,6 +138,7 @@ module API
mount ::API::PagesDomains
mount ::API::Pipelines
mount ::API::PipelineSchedules
+ mount ::API::ProjectImport
mount ::API::ProjectHooks
mount ::API::Projects
mount ::API::ProjectMilestones
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index d8fd6a6eb06..3d6e78d2d80 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -97,13 +97,16 @@ module API
end
params do
requires :sha, type: String, desc: 'A commit sha, or the name of a branch or tag'
+ use :pagination
end
get ':id/repository/commits/:sha/diff', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
- present commit.raw_diffs.to_a, with: Entities::Diff
+ raw_diffs = ::Kaminari.paginate_array(commit.raw_diffs.to_a)
+
+ present paginate(raw_diffs), with: Entities::Diff
end
desc "Get a commit's comments" do
@@ -156,6 +159,27 @@ module API
end
end
+ desc 'Get all references a commit is pushed to' do
+ detail 'This feature was introduced in GitLab 10.6'
+ success Entities::BasicRef
+ end
+ params do
+ requires :sha, type: String, desc: 'A commit sha'
+ optional :type, type: String, values: %w[branch tag all], default: 'all', desc: 'Scope'
+ use :pagination
+ end
+ get ':id/repository/commits/:sha/refs', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
+ commit = user_project.commit(params[:sha])
+ not_found!('Commit') unless commit
+
+ refs = []
+ refs.concat(user_project.repository.branch_names_contains(commit.id).map {|name| { type: 'branch', name: name }}) unless params[:type] == 'tag'
+ refs.concat(user_project.repository.tag_names_contains(commit.id).map {|name| { type: 'tag', name: name }}) unless params[:type] == 'branch'
+ refs = Kaminari.paginate_array(refs)
+
+ present paginate(refs), with: Entities::BasicRef
+ end
+
desc 'Post comment to commit' do
success Entities::CommitNote
end
@@ -165,7 +189,7 @@ module API
optional :path, type: String, desc: 'The file path'
given :path do
requires :line, type: Integer, desc: 'The line number'
- requires :line_type, type: String, values: %w(new old), default: 'new', desc: 'The type of the line'
+ requires :line_type, type: String, values: %w[new old], default: 'new', desc: 'The type of the line'
end
end
post ':id/repository/commits/:sha/comments', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 7838de13c56..167878ba600 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -22,6 +22,7 @@ module API
end
expose :avatar_path, if: ->(user, options) { options.fetch(:only_path, false) && user.avatar_path }
+ expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
expose :web_url do |user, options|
Gitlab::Routing.url_helpers.user_url(user)
@@ -90,6 +91,13 @@ module API
expose :created_at
end
+ class ProjectImportStatus < ProjectIdentity
+ expose :import_status
+
+ # TODO: Use `expose_nil` once we upgrade the grape-entity gem
+ expose :import_error, if: lambda { |status, _ops| status.import_error }
+ end
+
class BasicProjectDetails < ProjectIdentity
include ::API::ProjectsRelationBuilder
@@ -109,6 +117,8 @@ module API
expose :star_count, :forks_count
expose :last_activity_at
+ expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
+
def self.preload_relation(projects_relation, options = {})
projects_relation.preload(:project_feature, :route)
.preload(namespace: [:route, :owner],
@@ -230,6 +240,8 @@ module API
expose :parent_id
end
+ expose :custom_attributes, using: 'API::Entities::CustomAttribute', if: :with_custom_attributes
+
expose :statistics, if: :statistics do
with_options format_with: -> (value) { value.to_i } do
expose :storage_size
@@ -274,6 +286,11 @@ module API
expose :stats, using: Entities::CommitStats, if: :stats
expose :status
expose :last_pipeline, using: 'API::Entities::PipelineBasic'
+ expose :project_id
+ end
+
+ class BasicRef < Grape::Entity
+ expose :type, :name
end
class Branch < Grape::Entity
@@ -1137,6 +1154,10 @@ module API
expose :domain
expose :url
expose :project_id
+ expose :verified?, as: :verified
+ expose :verification_code, as: :verification_code
+ expose :enabled_until
+
expose :certificate,
as: :certificate_expiration,
if: ->(pages_domain, _) { pages_domain.certificate? },
@@ -1148,6 +1169,10 @@ module API
class PagesDomain < Grape::Entity
expose :domain
expose :url
+ expose :verified?, as: :verified
+ expose :verification_code, as: :verification_code
+ expose :enabled_until
+
expose :certificate,
if: ->(pages_domain, _) { pages_domain.certificate? },
using: PagesDomainCertificate do |pages_domain|
@@ -1172,6 +1197,7 @@ module API
expose :id
expose :ref
expose :startline
+ expose :project_id
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index b81f07a1770..4a4df1b8b9e 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -1,6 +1,7 @@
module API
class Groups < Grape::API
include PaginationParams
+ include Helpers::CustomAttributes
before { authenticate_non_get! }
@@ -67,6 +68,8 @@ module API
}
groups = groups.with_statistics if options[:statistics]
+ groups, options = with_custom_attributes(groups, options)
+
present paginate(groups), options
end
end
@@ -79,6 +82,7 @@ module API
end
params do
use :group_list_params
+ use :with_custom_attributes
end
get do
groups = find_groups(params)
@@ -142,9 +146,20 @@ module API
desc 'Get a single group, with containing projects.' do
success Entities::GroupDetail
end
+ params do
+ use :with_custom_attributes
+ end
get ":id" do
group = find_group!(params[:id])
- present group, with: Entities::GroupDetail, current_user: current_user
+
+ options = {
+ with: Entities::GroupDetail,
+ current_user: current_user
+ }
+
+ group, options = with_custom_attributes(group, options)
+
+ present group, options
end
desc 'Remove a group.'
@@ -175,12 +190,19 @@ module API
optional :starred, type: Boolean, default: false, desc: 'Limit by starred status'
use :pagination
+ use :with_custom_attributes
end
get ":id/projects" do
projects = find_group_projects(params)
- entity = params[:simple] ? Entities::BasicProjectDetails : Entities::Project
- present entity.prepare_relation(projects), with: entity, current_user: current_user
+ options = {
+ with: params[:simple] ? Entities::BasicProjectDetails : Entities::Project,
+ current_user: current_user
+ }
+
+ projects, options = with_custom_attributes(projects, options)
+
+ present options[:with].prepare_relation(projects), options
end
desc 'Get a list of subgroups in this group.' do
@@ -188,6 +210,7 @@ module API
end
params do
use :group_list_params
+ use :with_custom_attributes
end
get ":id/subgroups" do
groups = find_groups(params)
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index e75d8f1e6eb..de9058ce71f 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -172,7 +172,7 @@ module API
def find_project_snippet(id)
finder_params = { project: user_project }
- SnippetsFinder.new(current_user, finder_params).execute.find(id)
+ SnippetsFinder.new(current_user, finder_params).find(id)
end
def find_merge_request_with_access(iid, access_level = :read_merge_request)
diff --git a/lib/api/helpers/custom_attributes.rb b/lib/api/helpers/custom_attributes.rb
new file mode 100644
index 00000000000..70e4eda95f8
--- /dev/null
+++ b/lib/api/helpers/custom_attributes.rb
@@ -0,0 +1,28 @@
+module API
+ module Helpers
+ module CustomAttributes
+ extend ActiveSupport::Concern
+
+ included do
+ helpers do
+ params :with_custom_attributes do
+ optional :with_custom_attributes, type: Boolean, default: false, desc: 'Include custom attributes in the response'
+ end
+
+ def with_custom_attributes(collection_or_resource, options = {})
+ options = options.merge(
+ with_custom_attributes: params[:with_custom_attributes] &&
+ can?(current_user, :read_custom_attribute)
+ )
+
+ if options[:with_custom_attributes] && collection_or_resource.is_a?(ActiveRecord::Relation)
+ collection_or_resource = collection_or_resource.includes(:custom_attributes)
+ end
+
+ [collection_or_resource, options]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 9285fb90cdc..b3660e4a1d0 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -13,7 +13,7 @@ module API
# key_id - ssh key id for Git over SSH
# user_id - user id for Git over HTTP
# protocol - Git access protocol being used, e.g. HTTP or SSH
- # project - project path with namespace
+ # project - project full_path (not path on disk)
# action - git action (git-upload-pack or git-receive-pack)
# changes - changes as "oldrev newrev ref", see Gitlab::ChangesList
post "/allowed" do
diff --git a/lib/api/pages_domains.rb b/lib/api/pages_domains.rb
index d7b613a717e..ba33993d852 100644
--- a/lib/api/pages_domains.rb
+++ b/lib/api/pages_domains.rb
@@ -2,6 +2,8 @@ module API
class PagesDomains < Grape::API
include PaginationParams
+ PAGES_DOMAINS_ENDPOINT_REQUIREMENTS = API::PROJECT_ENDPOINT_REQUIREMENTS.merge(domain: API::NO_SLASH_URL_PART_REGEX)
+
before do
authenticate!
end
@@ -48,7 +50,7 @@ module API
params do
requires :id, type: String, desc: 'The ID of a project'
end
- resource :projects, requirements: { id: %r{[^/]+} } do
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
before do
require_pages_enabled!
end
@@ -71,7 +73,7 @@ module API
params do
requires :domain, type: String, desc: 'The domain'
end
- get ":id/pages/domains/:domain", requirements: { domain: %r{[^/]+} } do
+ get ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do
authorize! :read_pages, user_project
present pages_domain, with: Entities::PagesDomain
@@ -105,7 +107,7 @@ module API
optional :certificate, allow_blank: false, types: [File, String], desc: 'The certificate'
optional :key, allow_blank: false, types: [File, String], desc: 'The key'
end
- put ":id/pages/domains/:domain", requirements: { domain: %r{[^/]+} } do
+ put ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do
authorize! :update_pages, user_project
pages_domain_params = declared(params, include_parent_namespaces: false)
@@ -126,7 +128,7 @@ module API
params do
requires :domain, type: String, desc: 'The domain'
end
- delete ":id/pages/domains/:domain", requirements: { domain: %r{[^/]+} } do
+ delete ":id/pages/domains/:domain", requirements: PAGES_DOMAINS_ENDPOINT_REQUIREMENTS do
authorize! :update_pages, user_project
status 204
diff --git a/lib/api/project_import.rb b/lib/api/project_import.rb
new file mode 100644
index 00000000000..a509c1f32c1
--- /dev/null
+++ b/lib/api/project_import.rb
@@ -0,0 +1,69 @@
+module API
+ class ProjectImport < Grape::API
+ include PaginationParams
+
+ helpers do
+ def import_params
+ declared_params(include_missing: false)
+ end
+
+ def file_is_valid?
+ import_params[:file] && import_params[:file]['tempfile'].respond_to?(:read)
+ end
+
+ def validate_file!
+ render_api_error!('The file is invalid', 400) unless file_is_valid?
+ end
+ end
+
+ before do
+ forbidden! unless Gitlab::CurrentSettings.import_sources.include?('gitlab_project')
+ end
+
+ resource :projects, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
+ params do
+ requires :path, type: String, desc: 'The new project path and name'
+ requires :file, type: File, desc: 'The project export file to be imported'
+ optional :namespace, type: String, desc: "The ID or name of the namespace that the project will be imported into. Defaults to the current user's namespace."
+ end
+ desc 'Create a new project import' do
+ detail 'This feature was introduced in GitLab 10.6.'
+ success Entities::ProjectImportStatus
+ end
+ post 'import' do
+ validate_file!
+
+ Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42437')
+
+ namespace = if import_params[:namespace]
+ find_namespace!(import_params[:namespace])
+ else
+ current_user.namespace
+ end
+
+ project_params = {
+ path: import_params[:path],
+ namespace_id: namespace.id,
+ file: import_params[:file]['tempfile']
+ }
+
+ project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute
+
+ render_api_error!(project.errors.full_messages&.first, 400) unless project.saved?
+
+ present project, with: Entities::ProjectImportStatus
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ desc 'Get a project export status' do
+ detail 'This feature was introduced in GitLab 10.6.'
+ success Entities::ProjectImportStatus
+ end
+ get ':id/import' do
+ present user_project, with: Entities::ProjectImportStatus
+ end
+ end
+ end
+end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 5b481121a10..b552b0e0c5d 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -3,6 +3,7 @@ require_dependency 'declarative_policy'
module API
class Projects < Grape::API
include PaginationParams
+ include Helpers::CustomAttributes
before { authenticate_non_get! }
@@ -80,6 +81,7 @@ module API
projects = projects.with_merge_requests_enabled if params[:with_merge_requests_enabled]
projects = projects.with_statistics if params[:statistics]
projects = paginate(projects)
+ projects, options = with_custom_attributes(projects, options)
if current_user
project_members = current_user.project_members.preload(:source, user: [notification_settings: :source])
@@ -107,6 +109,7 @@ module API
requires :user_id, type: String, desc: 'The ID or username of the user'
use :collection_params
use :statistics_params
+ use :with_custom_attributes
end
get ":user_id/projects" do
user = find_user(params[:user_id])
@@ -127,6 +130,7 @@ module API
params do
use :collection_params
use :statistics_params
+ use :with_custom_attributes
end
get do
present_projects load_projects
@@ -196,11 +200,19 @@ module API
end
params do
use :statistics_params
+ use :with_custom_attributes
end
get ":id" do
- entity = current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails
- present user_project, with: entity, current_user: current_user,
- user_can_admin_project: can?(current_user, :admin_project, user_project), statistics: params[:statistics]
+ options = {
+ with: current_user ? Entities::ProjectWithAccess : Entities::BasicProjectDetails,
+ current_user: current_user,
+ user_can_admin_project: can?(current_user, :admin_project, user_project),
+ statistics: params[:statistics]
+ }
+
+ project, options = with_custom_attributes(user_project, options)
+
+ present project, options
end
desc 'Fork new project for the current user or provided namespace.' do
@@ -242,6 +254,7 @@ module API
end
params do
use :collection_params
+ use :with_custom_attributes
end
get ':id/forks' do
forks = ForkProjectsFinder.new(user_project, params: project_finder_params, current_user: current_user).execute
@@ -258,6 +271,7 @@ module API
[
:jobs_enabled,
:resolve_outdated_diff_discussions,
+ :ci_config_path,
:container_registry_enabled,
:default_branch,
:description,
diff --git a/lib/api/search.rb b/lib/api/search.rb
index 9f08fd96a3b..3556ad98c52 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -11,7 +11,7 @@ module API
projects: Entities::BasicProjectDetails,
milestones: Entities::Milestone,
notes: Entities::Note,
- commits: Entities::Commit,
+ commits: Entities::CommitDetail,
blobs: Entities::Blob,
wiki_blobs: Entities::Blob,
snippet_titles: Entities::Snippet,
@@ -35,7 +35,7 @@ module API
def process_results(results)
case params[:scope]
when 'wiki_blobs'
- paginate(results).map { |blob| Gitlab::ProjectSearchResults.parse_search_result(blob) }
+ paginate(results).map { |blob| Gitlab::ProjectSearchResults.parse_search_result(blob, user_project) }
when 'blobs'
paginate(results).map { |blob| blob[1] }
else
@@ -85,9 +85,7 @@ module API
use :pagination
end
get ':id/-/search' do
- find_group!(params[:id])
-
- present search(group_id: params[:id]), with: entity
+ present search(group_id: user_group.id), with: entity
end
end
@@ -106,9 +104,7 @@ module API
use :pagination
end
get ':id/-/search' do
- find_project!(params[:id])
-
- present search(project_id: params[:id]), with: entity
+ present search(project_id: user_project.id), with: entity
end
end
end
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index cee4d309816..152df23a327 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -147,7 +147,7 @@ module API
attrs[:password_authentication_enabled_for_web] = attrs.delete(:password_authentication_enabled)
end
- if current_settings.update_attributes(attrs)
+ if ApplicationSettings::UpdateService.new(current_settings, current_user, attrs).execute
present current_settings, with: Entities::ApplicationSetting
else
render_validation_error!(current_settings)
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index ffccfebe752..c6dbcf84e3a 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -60,7 +60,7 @@ module API
end
post ':id/mark_as_done' do
TodoService.new.mark_todos_as_done_by_ids(params[:id], current_user)
- todo = Todo.find(params[:id])
+ todo = current_user.todos.find(params[:id])
present todo, with: Entities::Todo, current_user: current_user
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 3cc12724b8a..3920171205f 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -2,6 +2,7 @@ module API
class Users < Grape::API
include PaginationParams
include APIGuard
+ include Helpers::CustomAttributes
allow_access_with_scope :read_user, if: -> (request) { request.get? }
@@ -70,6 +71,7 @@ module API
use :sort_params
use :pagination
+ use :with_custom_attributes
end
get do
authenticated_as_admin! if params[:external].present? || (params[:extern_uid].present? && params[:provider].present?)
@@ -94,8 +96,9 @@ module API
entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic
users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin
+ users, options = with_custom_attributes(users, with: entity)
- present paginate(users), with: entity
+ present paginate(users), options
end
desc 'Get a single user' do
@@ -103,12 +106,16 @@ module API
end
params do
requires :id, type: Integer, desc: 'The ID of the user'
+
+ use :with_custom_attributes
end
get ":id" do
user = User.find_by(id: params[:id])
not_found!('User') unless user && can?(current_user, :read_user, user)
opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User }
+ user, opts = with_custom_attributes(user, opts)
+
present user, opts
end
diff --git a/lib/api/v3/todos.rb b/lib/api/v3/todos.rb
index 2f2cf259987..3e2c61f6dbd 100644
--- a/lib/api/v3/todos.rb
+++ b/lib/api/v3/todos.rb
@@ -12,7 +12,7 @@ module API
end
delete ':id' do
TodoService.new.mark_todos_as_done_by_ids(params[:id], current_user)
- todo = Todo.find(params[:id])
+ todo = current_user.todos.find(params[:id])
present todo, with: ::API::Entities::Todo, current_user: current_user
end
diff --git a/lib/banzai/filter/html_entity_filter.rb b/lib/banzai/filter/html_entity_filter.rb
index f3bd587c28b..e008fd428b0 100644
--- a/lib/banzai/filter/html_entity_filter.rb
+++ b/lib/banzai/filter/html_entity_filter.rb
@@ -5,7 +5,7 @@ module Banzai
# Text filter that escapes these HTML entities: & " < >
class HtmlEntityFilter < HTML::Pipeline::TextFilter
def call
- ERB::Util.html_escape_once(text)
+ ERB::Util.html_escape(text)
end
end
end
diff --git a/lib/banzai/filter/issuable_state_filter.rb b/lib/banzai/filter/issuable_state_filter.rb
index 327ea9449a1..77299abe324 100644
--- a/lib/banzai/filter/issuable_state_filter.rb
+++ b/lib/banzai/filter/issuable_state_filter.rb
@@ -15,6 +15,8 @@ module Banzai
issuables = extractor.extract([doc])
issuables.each do |node, issuable|
+ next if !can_read_cross_project? && issuable.project != project
+
if VISIBLE_STATES.include?(issuable.state) && node.inner_html == issuable.reference_link_text(project)
node.content += " (#{issuable.state})"
end
@@ -25,6 +27,10 @@ module Banzai
private
+ def can_read_cross_project?
+ Ability.allowed?(current_user, :read_cross_project)
+ end
+
def current_user
context[:current_user]
end
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index 2a6b0964ac5..8ec696ce5fc 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -64,7 +64,7 @@ module Banzai
finder_params[:group_ids] = [project.group.id]
end
- MilestonesFinder.new(finder_params).execute.find_by(params)
+ MilestonesFinder.new(finder_params).find_by(params)
end
def url_for_object(milestone, project)
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index a79a0154846..0ac7e231b5b 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -14,23 +14,33 @@ module Banzai
end
def highlight_node(node)
- code = node.text
css_classes = 'code highlight js-syntax-highlight'
- language = node.attr('lang')
+ lang = node.attr('lang')
+ retried = false
- if use_rouge?(language)
- lexer = lexer_for(language)
+ if use_rouge?(lang)
+ lexer = lexer_for(lang)
language = lexer.tag
+ else
+ lexer = Rouge::Lexers::PlainText.new
+ language = lang
+ end
+
+ begin
+ code = Rouge::Formatters::HTMLGitlab.format(lex(lexer, node.text), tag: language)
+ css_classes << " #{language}" if language
+ rescue
+ # Gracefully handle syntax highlighter bugs/errors to ensure users can
+ # still access an issue/comment/etc. First, retry with the plain text
+ # filter. If that fails, then just skip this entirely, but that would
+ # be a pretty bad upstream bug.
+ return if retried
- begin
- code = Rouge::Formatters::HTMLGitlab.format(lex(lexer, code), tag: language)
- css_classes << " #{language}"
- rescue
- # Gracefully handle syntax highlighter bugs/errors to ensure
- # users can still access an issue/comment/etc.
+ language = nil
+ lexer = Rouge::Lexers::PlainText.new
+ retried = true
- language = nil
- end
+ retry
end
highlighted = %(<pre class="#{css_classes}" lang="#{language}" v-pre="true"><code>#{code}</code></pre>)
diff --git a/lib/banzai/redactor.rb b/lib/banzai/redactor.rb
index de3ebe72720..827df7c08ae 100644
--- a/lib/banzai/redactor.rb
+++ b/lib/banzai/redactor.rb
@@ -19,8 +19,9 @@ module Banzai
#
# Returns the documents passed as the first argument.
def redact(documents)
- all_document_nodes = document_nodes(documents)
+ redact_cross_project_references(documents) unless can_read_cross_project?
+ all_document_nodes = document_nodes(documents)
redact_document_nodes(all_document_nodes)
end
@@ -51,6 +52,18 @@ module Banzai
metadata
end
+ def redact_cross_project_references(documents)
+ extractor = Banzai::IssuableExtractor.new(project, user)
+ issuables = extractor.extract(documents)
+
+ issuables.each do |node, issuable|
+ next if issuable.project == project
+
+ node['class'] = node['class'].gsub('has-tooltip', '')
+ node['title'] = nil
+ end
+ end
+
# Returns the nodes visible to the current user.
#
# nodes - The input nodes to check.
@@ -78,5 +91,11 @@ module Banzai
{ document: document, nodes: Querying.css(document, 'a.gfm[data-reference-type]') }
end
end
+
+ private
+
+ def can_read_cross_project?
+ Ability.allowed?(user, :read_cross_project)
+ end
end
end
diff --git a/lib/banzai/reference_parser/issuable_parser.rb b/lib/banzai/reference_parser/issuable_parser.rb
index 3953867eb83..fad127d7e5b 100644
--- a/lib/banzai/reference_parser/issuable_parser.rb
+++ b/lib/banzai/reference_parser/issuable_parser.rb
@@ -18,7 +18,7 @@ module Banzai
end
def can_read_reference?(user, issuable)
- can?(user, "read_#{issuable.class.to_s.underscore}".to_sym, issuable)
+ can?(user, "read_#{issuable.class.to_s.underscore}_iid".to_sym, issuable)
end
end
end
diff --git a/lib/banzai/reference_parser/issue_parser.rb b/lib/banzai/reference_parser/issue_parser.rb
index 38d4e3f3e44..230827129b6 100644
--- a/lib/banzai/reference_parser/issue_parser.rb
+++ b/lib/banzai/reference_parser/issue_parser.rb
@@ -5,12 +5,31 @@ module Banzai
def nodes_visible_to_user(user, nodes)
issues = records_for_nodes(nodes)
+ issues_to_check = issues.values
- readable_issues = Ability
- .issues_readable_by_user(issues.values, user).to_set
+ unless can?(user, :read_cross_project)
+ issues_to_check, cross_project_issues = issues_to_check.partition do |issue|
+ issue.project == project
+ end
+ end
+
+ readable_issues = Ability.issues_readable_by_user(issues_to_check, user).to_set
nodes.select do |node|
- readable_issues.include?(issues[node])
+ issue_in_node = issues[node]
+
+ # We check the inclusion of readable issues first because it's faster.
+ #
+ # But we need to fall back to `read_issue_iid` if the user cannot read
+ # cross project, since it might be possible the user can see the IID
+ # but not the issue.
+ if readable_issues.include?(issue_in_node)
+ true
+ elsif cross_project_issues&.include?(issue_in_node)
+ can_read_reference?(user, issue_in_node)
+ else
+ false
+ end
end
end
diff --git a/lib/gitlab/asciidoc.rb b/lib/gitlab/asciidoc.rb
index ee7f4be6b9f..62c41801d75 100644
--- a/lib/gitlab/asciidoc.rb
+++ b/lib/gitlab/asciidoc.rb
@@ -8,7 +8,8 @@ module Gitlab
module Asciidoc
DEFAULT_ADOC_ATTRS = [
'showtitle', 'idprefix=user-content-', 'idseparator=-', 'env=gitlab',
- 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font'
+ 'env-gitlab', 'source-highlighter=html-pipeline', 'icons=font',
+ 'outfilesuffix=.adoc'
].freeze
# Public: Converts the provided Asciidoc markup into HTML.
diff --git a/lib/gitlab/auth/request_authenticator.rb b/lib/gitlab/auth/request_authenticator.rb
index 46ec040ce92..a0b5cd868c3 100644
--- a/lib/gitlab/auth/request_authenticator.rb
+++ b/lib/gitlab/auth/request_authenticator.rb
@@ -20,6 +20,14 @@ module Gitlab
rescue Gitlab::Auth::AuthenticationError
nil
end
+
+ def valid_access_token?(scopes: [])
+ validate_access_token!(scopes: scopes)
+
+ true
+ rescue Gitlab::Auth::AuthenticationError
+ false
+ end
end
end
end
diff --git a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
index 03b17b319fa..1b4a9e8a194 100644
--- a/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
+++ b/lib/gitlab/background_migration/create_fork_network_memberships_range.rb
@@ -14,6 +14,14 @@ module Gitlab
def perform(start_id, end_id)
log("Creating memberships for forks: #{start_id} - #{end_id}")
+ insert_members(start_id, end_id)
+
+ if missing_members?(start_id, end_id)
+ BackgroundMigrationWorker.perform_in(RESCHEDULE_DELAY, "CreateForkNetworkMembershipsRange", [start_id, end_id])
+ end
+ end
+
+ def insert_members(start_id, end_id)
ActiveRecord::Base.connection.execute <<~INSERT_MEMBERS
INSERT INTO fork_network_members (fork_network_id, project_id, forked_from_project_id)
@@ -33,10 +41,9 @@ module Gitlab
WHERE existing_members.project_id = forked_project_links.forked_to_project_id
)
INSERT_MEMBERS
-
- if missing_members?(start_id, end_id)
- BackgroundMigrationWorker.perform_in(RESCHEDULE_DELAY, "CreateForkNetworkMembershipsRange", [start_id, end_id])
- end
+ rescue ActiveRecord::RecordNotUnique => e
+ # `fork_network_member` was created concurrently in another migration
+ log(e.message)
end
def missing_members?(start_id, end_id)
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads.rb b/lib/gitlab/background_migration/populate_untracked_uploads.rb
index 8a8e770940e..9232f20a063 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads.rb
@@ -5,157 +5,10 @@ module Gitlab
# This class processes a batch of rows in `untracked_files_for_uploads` by
# adding each file to the `uploads` table if it does not exist.
class PopulateUntrackedUploads # rubocop:disable Metrics/ClassLength
- # This class is responsible for producing the attributes necessary to
- # track an uploaded file in the `uploads` table.
- class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength, Metrics/LineLength
- self.table_name = 'untracked_files_for_uploads'
-
- # Ends with /:random_hex/:filename
- FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z}
- FULL_PATH_CAPTURE = /\A(.+)#{FILE_UPLOADER_PATH}/
-
- # These regex patterns are tested against a relative path, relative to
- # the upload directory.
- # For convenience, if there exists a capture group in the pattern, then
- # it indicates the model_id.
- PATH_PATTERNS = [
- {
- pattern: %r{\A-/system/appearance/logo/(\d+)/},
- uploader: 'AttachmentUploader',
- model_type: 'Appearance'
- },
- {
- pattern: %r{\A-/system/appearance/header_logo/(\d+)/},
- uploader: 'AttachmentUploader',
- model_type: 'Appearance'
- },
- {
- pattern: %r{\A-/system/note/attachment/(\d+)/},
- uploader: 'AttachmentUploader',
- model_type: 'Note'
- },
- {
- pattern: %r{\A-/system/user/avatar/(\d+)/},
- uploader: 'AvatarUploader',
- model_type: 'User'
- },
- {
- pattern: %r{\A-/system/group/avatar/(\d+)/},
- uploader: 'AvatarUploader',
- model_type: 'Namespace'
- },
- {
- pattern: %r{\A-/system/project/avatar/(\d+)/},
- uploader: 'AvatarUploader',
- model_type: 'Project'
- },
- {
- pattern: FILE_UPLOADER_PATH,
- uploader: 'FileUploader',
- model_type: 'Project'
- }
- ].freeze
-
- def to_h
- @upload_hash ||= {
- path: upload_path,
- uploader: uploader,
- model_type: model_type,
- model_id: model_id,
- size: file_size,
- checksum: checksum
- }
- end
-
- def upload_path
- # UntrackedFile#path is absolute, but Upload#path depends on uploader
- @upload_path ||=
- if uploader == 'FileUploader'
- # Path relative to project directory in uploads
- matchd = path_relative_to_upload_dir.match(FILE_UPLOADER_PATH)
- matchd[0].sub(%r{\A/}, '') # remove leading slash
- else
- path
- end
- end
-
- def uploader
- matching_pattern_map[:uploader]
- end
-
- def model_type
- matching_pattern_map[:model_type]
- end
-
- def model_id
- return @model_id if defined?(@model_id)
-
- pattern = matching_pattern_map[:pattern]
- matchd = path_relative_to_upload_dir.match(pattern)
-
- # If something is captured (matchd[1] is not nil), it is a model_id
- # Only the FileUploader pattern will not match an ID
- @model_id = matchd[1] ? matchd[1].to_i : file_uploader_model_id
- end
-
- def file_size
- File.size(absolute_path)
- end
-
- def checksum
- Digest::SHA256.file(absolute_path).hexdigest
- end
-
- private
-
- def matching_pattern_map
- @matching_pattern_map ||= PATH_PATTERNS.find do |path_pattern_map|
- path_relative_to_upload_dir.match(path_pattern_map[:pattern])
- end
-
- unless @matching_pattern_map
- raise "Unknown upload path pattern \"#{path}\""
- end
-
- @matching_pattern_map
- end
-
- def file_uploader_model_id
- matchd = path_relative_to_upload_dir.match(FULL_PATH_CAPTURE)
- not_found_msg = <<~MSG
- Could not capture project full_path from a FileUploader path:
- "#{path_relative_to_upload_dir}"
- MSG
- raise not_found_msg unless matchd
-
- full_path = matchd[1]
- project = Project.find_by_full_path(full_path)
- return nil unless project
-
- project.id
- end
-
- # Not including a leading slash
- def path_relative_to_upload_dir
- upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR # rubocop:disable Metrics/LineLength
- base = %r{\A#{Regexp.escape(upload_dir)}/}
- @path_relative_to_upload_dir ||= path.sub(base, '')
- end
-
- def absolute_path
- File.join(Gitlab.config.uploads.storage_path, path)
- end
- end
-
- # This class is used to query the `uploads` table.
- class Upload < ActiveRecord::Base
- self.table_name = 'uploads'
- end
-
def perform(start_id, end_id)
return unless migrate?
- files = UntrackedFile.where(id: start_id..end_id)
+ files = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.where(id: start_id..end_id)
processed_files = insert_uploads_if_needed(files)
processed_files.delete_all
@@ -165,7 +18,8 @@ module Gitlab
private
def migrate?
- UntrackedFile.table_exists? && Upload.table_exists?
+ Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.table_exists? &&
+ Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Upload.table_exists?
end
def insert_uploads_if_needed(files)
@@ -197,7 +51,7 @@ module Gitlab
def filter_existing_uploads(files)
paths = files.map(&:upload_path)
- existing_paths = Upload.where(path: paths).pluck(:path).to_set
+ existing_paths = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Upload.where(path: paths).pluck(:path).to_set
files.reject do |file|
existing_paths.include?(file.upload_path)
@@ -229,7 +83,7 @@ module Gitlab
end
ids.each do |model_type, model_ids|
- model_class = Object.const_get(model_type)
+ model_class = "Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::#{model_type}".constantize
found_ids = model_class.where(id: model_ids.uniq).pluck(:id)
deleted_ids = ids[model_type] - found_ids
ids[model_type] = deleted_ids
@@ -249,8 +103,8 @@ module Gitlab
end
def drop_temp_table_if_finished
- if UntrackedFile.all.empty?
- UntrackedFile.connection.drop_table(:untracked_files_for_uploads,
+ if Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.all.empty? && !Rails.env.test? # Dropping a table intermittently breaks test cleanup
+ Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile.connection.drop_table(:untracked_files_for_uploads,
if_exists: true)
end
end
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
new file mode 100644
index 00000000000..a2c5acbde71
--- /dev/null
+++ b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
@@ -0,0 +1,201 @@
+# frozen_string_literal: true
+module Gitlab
+ module BackgroundMigration
+ module PopulateUntrackedUploadsDependencies
+ # This class is responsible for producing the attributes necessary to
+ # track an uploaded file in the `uploads` table.
+ class UntrackedFile < ActiveRecord::Base # rubocop:disable Metrics/ClassLength, Metrics/LineLength
+ self.table_name = 'untracked_files_for_uploads'
+
+ # Ends with /:random_hex/:filename
+ FILE_UPLOADER_PATH = %r{/\h+/[^/]+\z}
+ FULL_PATH_CAPTURE = /\A(.+)#{FILE_UPLOADER_PATH}/
+
+ # These regex patterns are tested against a relative path, relative to
+ # the upload directory.
+ # For convenience, if there exists a capture group in the pattern, then
+ # it indicates the model_id.
+ PATH_PATTERNS = [
+ {
+ pattern: %r{\A-/system/appearance/logo/(\d+)/},
+ uploader: 'AttachmentUploader',
+ model_type: 'Appearance'
+ },
+ {
+ pattern: %r{\A-/system/appearance/header_logo/(\d+)/},
+ uploader: 'AttachmentUploader',
+ model_type: 'Appearance'
+ },
+ {
+ pattern: %r{\A-/system/note/attachment/(\d+)/},
+ uploader: 'AttachmentUploader',
+ model_type: 'Note'
+ },
+ {
+ pattern: %r{\A-/system/user/avatar/(\d+)/},
+ uploader: 'AvatarUploader',
+ model_type: 'User'
+ },
+ {
+ pattern: %r{\A-/system/group/avatar/(\d+)/},
+ uploader: 'AvatarUploader',
+ model_type: 'Namespace'
+ },
+ {
+ pattern: %r{\A-/system/project/avatar/(\d+)/},
+ uploader: 'AvatarUploader',
+ model_type: 'Project'
+ },
+ {
+ pattern: FILE_UPLOADER_PATH,
+ uploader: 'FileUploader',
+ model_type: 'Project'
+ }
+ ].freeze
+
+ def to_h
+ @upload_hash ||= {
+ path: upload_path,
+ uploader: uploader,
+ model_type: model_type,
+ model_id: model_id,
+ size: file_size,
+ checksum: checksum
+ }
+ end
+
+ def upload_path
+ # UntrackedFile#path is absolute, but Upload#path depends on uploader
+ @upload_path ||=
+ if uploader == 'FileUploader'
+ # Path relative to project directory in uploads
+ matchd = path_relative_to_upload_dir.match(FILE_UPLOADER_PATH)
+ matchd[0].sub(%r{\A/}, '') # remove leading slash
+ else
+ path
+ end
+ end
+
+ def uploader
+ matching_pattern_map[:uploader]
+ end
+
+ def model_type
+ matching_pattern_map[:model_type]
+ end
+
+ def model_id
+ return @model_id if defined?(@model_id)
+
+ pattern = matching_pattern_map[:pattern]
+ matchd = path_relative_to_upload_dir.match(pattern)
+
+ # If something is captured (matchd[1] is not nil), it is a model_id
+ # Only the FileUploader pattern will not match an ID
+ @model_id = matchd[1] ? matchd[1].to_i : file_uploader_model_id
+ end
+
+ def file_size
+ File.size(absolute_path)
+ end
+
+ def checksum
+ Digest::SHA256.file(absolute_path).hexdigest
+ end
+
+ private
+
+ def matching_pattern_map
+ @matching_pattern_map ||= PATH_PATTERNS.find do |path_pattern_map|
+ path_relative_to_upload_dir.match(path_pattern_map[:pattern])
+ end
+
+ unless @matching_pattern_map
+ raise "Unknown upload path pattern \"#{path}\""
+ end
+
+ @matching_pattern_map
+ end
+
+ def file_uploader_model_id
+ matchd = path_relative_to_upload_dir.match(FULL_PATH_CAPTURE)
+ not_found_msg = <<~MSG
+ Could not capture project full_path from a FileUploader path:
+ "#{path_relative_to_upload_dir}"
+ MSG
+ raise not_found_msg unless matchd
+
+ full_path = matchd[1]
+ project = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Project.find_by_full_path(full_path)
+ return nil unless project
+
+ project.id
+ end
+
+ # Not including a leading slash
+ def path_relative_to_upload_dir
+ upload_dir = Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR # rubocop:disable Metrics/LineLength
+ base = %r{\A#{Regexp.escape(upload_dir)}/}
+ @path_relative_to_upload_dir ||= path.sub(base, '')
+ end
+
+ def absolute_path
+ File.join(Gitlab.config.uploads.storage_path, path)
+ end
+ end
+
+ # Avoid using application code
+ class Upload < ActiveRecord::Base
+ self.table_name = 'uploads'
+ end
+
+ # Avoid using application code
+ class Appearance < ActiveRecord::Base
+ self.table_name = 'appearances'
+ end
+
+ # Avoid using application code
+ class Namespace < ActiveRecord::Base
+ self.table_name = 'namespaces'
+ end
+
+ # Avoid using application code
+ class Note < ActiveRecord::Base
+ self.table_name = 'notes'
+ end
+
+ # Avoid using application code
+ class User < ActiveRecord::Base
+ self.table_name = 'users'
+ end
+
+ # Since project Markdown upload paths don't contain the project ID, we have to find the
+ # project by its full_path. Due to MySQL/PostgreSQL differences, and historical reasons,
+ # the logic is somewhat complex, so I've mostly copied it in here.
+ class Project < ActiveRecord::Base
+ self.table_name = 'projects'
+
+ def self.find_by_full_path(path)
+ binary = Gitlab::Database.mysql? ? 'BINARY' : ''
+ order_sql = "(CASE WHEN #{binary} routes.path = #{connection.quote(path)} THEN 0 ELSE 1 END)"
+ where_full_path_in(path).reorder(order_sql).take
+ end
+
+ def self.where_full_path_in(path)
+ cast_lower = Gitlab::Database.postgresql?
+
+ path = connection.quote(path)
+
+ where =
+ if cast_lower
+ "(LOWER(routes.path) = LOWER(#{path}))"
+ else
+ "(routes.path = #{path})"
+ end
+
+ joins("INNER JOIN routes ON routes.source_id = projects.id AND routes.source_type = 'Project'").where(where)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/prepare_untracked_uploads.rb b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
index a7a1bbe1752..914a9e48a2f 100644
--- a/lib/gitlab/background_migration/prepare_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/prepare_untracked_uploads.rb
@@ -43,7 +43,11 @@ module Gitlab
store_untracked_file_paths
- schedule_populate_untracked_uploads_jobs
+ if UntrackedFile.all.empty?
+ drop_temp_table
+ else
+ schedule_populate_untracked_uploads_jobs
+ end
end
private
@@ -92,7 +96,7 @@ module Gitlab
end
end
- yield(paths)
+ yield(paths) if paths.any?
end
def build_find_command(search_dir)
@@ -165,6 +169,13 @@ module Gitlab
bulk_queue_background_migration_jobs_by_range(
UntrackedFile, FOLLOW_UP_MIGRATION)
end
+
+ def drop_temp_table
+ unless Rails.env.test? # Dropping a table intermittently breaks test cleanup
+ UntrackedFile.connection.drop_table(:untracked_files_for_uploads,
+ if_exists: true)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/checks/change_access.rb b/lib/gitlab/checks/change_access.rb
index d75e73dac10..3ce5f807989 100644
--- a/lib/gitlab/checks/change_access.rb
+++ b/lib/gitlab/checks/change_access.rb
@@ -16,11 +16,11 @@ module Gitlab
lfs_objects_missing: 'LFS objects are missing. Ensure LFS is properly set up or try a manual "git lfs push --all".'
}.freeze
- attr_reader :user_access, :project, :skip_authorization, :protocol, :oldrev, :newrev, :ref, :branch_name, :tag_name
+ attr_reader :user_access, :project, :skip_authorization, :skip_lfs_integrity_check, :protocol, :oldrev, :newrev, :ref, :branch_name, :tag_name
def initialize(
change, user_access:, project:, skip_authorization: false,
- protocol:
+ skip_lfs_integrity_check: false, protocol:
)
@oldrev, @newrev, @ref = change.values_at(:oldrev, :newrev, :ref)
@branch_name = Gitlab::Git.branch_name(@ref)
@@ -28,6 +28,7 @@ module Gitlab
@user_access = user_access
@project = project
@skip_authorization = skip_authorization
+ @skip_lfs_integrity_check = skip_lfs_integrity_check
@protocol = protocol
end
@@ -37,7 +38,7 @@ module Gitlab
push_checks
branch_checks
tag_checks
- lfs_objects_exist_check
+ lfs_objects_exist_check unless skip_lfs_integrity_check
commits_check unless skip_commits_check
true
@@ -120,6 +121,7 @@ module Gitlab
def commits_check
return if deletion? || newrev.nil?
+ return unless should_run_commit_validations?
# n+1: https://gitlab.com/gitlab-org/gitlab-ee/issues/3593
::Gitlab::GitalyClient.allow_n_plus_1_calls do
@@ -138,6 +140,10 @@ module Gitlab
private
+ def should_run_commit_validations?
+ commit_check.validate_lfs_file_locks?
+ end
+
def updated_from_web?
protocol == 'web'
end
@@ -175,7 +181,7 @@ module Gitlab
end
def commits
- project.repository.new_commits(newrev)
+ @commits ||= project.repository.new_commits(newrev)
end
end
end
diff --git a/lib/gitlab/checks/commit_check.rb b/lib/gitlab/checks/commit_check.rb
index ae0cd142378..43a52b493bb 100644
--- a/lib/gitlab/checks/commit_check.rb
+++ b/lib/gitlab/checks/commit_check.rb
@@ -35,14 +35,14 @@ module Gitlab
end
end
- private
-
def validate_lfs_file_locks?
strong_memoize(:validate_lfs_file_locks) do
project.lfs_enabled? && project.lfs_file_locks.any? && newrev && oldrev
end
end
+ private
+
def lfs_file_locks_validation
lambda do |paths|
lfs_lock = project.lfs_file_locks.where(path: paths).where.not(user_id: user.id).first
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 0735243e021..9576d5a3fd8 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -34,6 +34,8 @@ module Gitlab
end
def events_by_date(date)
+ return Event.none unless can_read_cross_project?
+
events = Event.contributions.where(author_id: contributor.id)
.where(created_at: date.beginning_of_day..date.end_of_day)
.where(project_id: projects)
@@ -53,6 +55,10 @@ module Gitlab
private
+ def can_read_cross_project?
+ Ability.allowed?(current_user, :read_cross_project)
+ end
+
def event_counts(date_from, feature)
t = Event.arel_table
diff --git a/lib/gitlab/cross_project_access.rb b/lib/gitlab/cross_project_access.rb
new file mode 100644
index 00000000000..6eaed51b64c
--- /dev/null
+++ b/lib/gitlab/cross_project_access.rb
@@ -0,0 +1,67 @@
+module Gitlab
+ class CrossProjectAccess
+ class << self
+ delegate :add_check, :find_check, :checks,
+ to: :instance
+ end
+
+ def self.instance
+ @instance ||= new
+ end
+
+ attr_reader :checks
+
+ def initialize
+ @checks = {}
+ end
+
+ def add_check(
+ klass,
+ actions: {},
+ positive_condition: nil,
+ negative_condition: nil,
+ skip: false)
+
+ new_check = CheckInfo.new(actions,
+ positive_condition,
+ negative_condition,
+ skip
+ )
+
+ @checks[klass] ||= Gitlab::CrossProjectAccess::CheckCollection.new
+ @checks[klass].add_check(new_check)
+ recalculate_checks_for_class(klass)
+
+ @checks[klass]
+ end
+
+ def find_check(object)
+ @cached_checks ||= Hash.new do |cache, new_class|
+ parent_classes = @checks.keys.select { |existing_class| new_class <= existing_class }
+ closest_class = closest_parent(parent_classes, new_class)
+ cache[new_class] = @checks[closest_class]
+ end
+
+ @cached_checks[object.class]
+ end
+
+ private
+
+ def recalculate_checks_for_class(klass)
+ new_collection = @checks[klass]
+
+ @checks.each do |existing_class, existing_check_collection|
+ if existing_class < klass
+ existing_check_collection.add_collection(new_collection)
+ elsif klass < existing_class
+ new_collection.add_collection(existing_check_collection)
+ end
+ end
+ end
+
+ def closest_parent(classes, subject)
+ relevant_ancestors = subject.ancestors & classes
+ relevant_ancestors.first
+ end
+ end
+end
diff --git a/lib/gitlab/cross_project_access/check_collection.rb b/lib/gitlab/cross_project_access/check_collection.rb
new file mode 100644
index 00000000000..88376232065
--- /dev/null
+++ b/lib/gitlab/cross_project_access/check_collection.rb
@@ -0,0 +1,47 @@
+module Gitlab
+ class CrossProjectAccess
+ class CheckCollection
+ attr_reader :checks
+
+ def initialize
+ @checks = []
+ end
+
+ def add_collection(collection)
+ @checks |= collection.checks
+ end
+
+ def add_check(check)
+ @checks << check
+ end
+
+ def should_run?(object)
+ skips, runs = arranged_checks
+
+ # If one rule tells us to skip, we skip the cross project check
+ return false if skips.any? { |check| check.should_skip?(object) }
+
+ # If the rule isn't skipped, we run it if any of the checks says we
+ # should run
+ runs.any? { |check| check.should_run?(object) }
+ end
+
+ def arranged_checks
+ return [@skips, @runs] if @skips && @runs
+
+ @skips = []
+ @runs = []
+
+ @checks.each do |check|
+ if check.skip
+ @skips << check
+ else
+ @runs << check
+ end
+ end
+
+ [@skips, @runs]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cross_project_access/check_info.rb b/lib/gitlab/cross_project_access/check_info.rb
new file mode 100644
index 00000000000..e8a845c7f1e
--- /dev/null
+++ b/lib/gitlab/cross_project_access/check_info.rb
@@ -0,0 +1,66 @@
+module Gitlab
+ class CrossProjectAccess
+ class CheckInfo
+ attr_accessor :actions, :positive_condition, :negative_condition, :skip
+
+ def initialize(actions, positive_condition, negative_condition, skip)
+ @actions = actions
+ @positive_condition = positive_condition
+ @negative_condition = negative_condition
+ @skip = skip
+ end
+
+ def should_skip?(object)
+ return !should_run?(object) unless @skip
+
+ skip_for_action = @actions[current_action(object)]
+ skip_for_action = false if @actions[current_action(object)].nil?
+
+ # We need to do the opposite of what was defined in the following cases:
+ # - skip_cross_project_access_check index: true, if: -> { false }
+ # - skip_cross_project_access_check index: true, unless: -> { true }
+ if positive_condition_is_false?(object)
+ skip_for_action = !skip_for_action
+ end
+
+ if negative_condition_is_true?(object)
+ skip_for_action = !skip_for_action
+ end
+
+ skip_for_action
+ end
+
+ def should_run?(object)
+ return !should_skip?(object) if @skip
+
+ run_for_action = @actions[current_action(object)]
+ run_for_action = true if @actions[current_action(object)].nil?
+
+ # We need to do the opposite of what was defined in the following cases:
+ # - requires_cross_project_access index: true, if: -> { false }
+ # - requires_cross_project_access index: true, unless: -> { true }
+ if positive_condition_is_false?(object)
+ run_for_action = !run_for_action
+ end
+
+ if negative_condition_is_true?(object)
+ run_for_action = !run_for_action
+ end
+
+ run_for_action
+ end
+
+ def positive_condition_is_false?(object)
+ @positive_condition && !object.instance_exec(&@positive_condition)
+ end
+
+ def negative_condition_is_true?(object)
+ @negative_condition && object.instance_exec(&@negative_condition)
+ end
+
+ def current_action(object)
+ object.respond_to?(:action_name) ? object.action_name.to_sym : nil
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/cross_project_access/class_methods.rb b/lib/gitlab/cross_project_access/class_methods.rb
new file mode 100644
index 00000000000..90eac94800c
--- /dev/null
+++ b/lib/gitlab/cross_project_access/class_methods.rb
@@ -0,0 +1,48 @@
+module Gitlab
+ class CrossProjectAccess
+ module ClassMethods
+ def requires_cross_project_access(*args)
+ positive_condition, negative_condition, actions = extract_params(args)
+
+ Gitlab::CrossProjectAccess.add_check(
+ self,
+ actions: actions,
+ positive_condition: positive_condition,
+ negative_condition: negative_condition
+ )
+ end
+
+ def skip_cross_project_access_check(*args)
+ positive_condition, negative_condition, actions = extract_params(args)
+
+ Gitlab::CrossProjectAccess.add_check(
+ self,
+ actions: actions,
+ positive_condition: positive_condition,
+ negative_condition: negative_condition,
+ skip: true
+ )
+ end
+
+ private
+
+ def extract_params(args)
+ actions = {}
+ positive_condition = nil
+ negative_condition = nil
+
+ args.each do |argument|
+ if argument.is_a?(Hash)
+ positive_condition = argument.delete(:if)
+ negative_condition = argument.delete(:unless)
+ actions.merge!(argument)
+ else
+ actions[argument] = true
+ end
+ end
+
+ [positive_condition, negative_condition, actions]
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
index 0f897e6316c..269016daac2 100644
--- a/lib/gitlab/diff/highlight.rb
+++ b/lib/gitlab/diff/highlight.rb
@@ -27,7 +27,17 @@ module Gitlab
rich_line = highlight_line(diff_line) || diff_line.text
if line_inline_diffs = inline_diffs[i]
- rich_line = InlineDiffMarker.new(diff_line.text, rich_line).mark(line_inline_diffs)
+ begin
+ rich_line = InlineDiffMarker.new(diff_line.text, rich_line).mark(line_inline_diffs)
+ # This should only happen when the encoding of the diff doesn't
+ # match the blob, which is a bug. But we shouldn't fail to render
+ # completely in that case, even though we want to report the error.
+ rescue RangeError => e
+ if Gitlab::Sentry.enabled?
+ Gitlab::Sentry.context
+ Raven.capture_exception(e)
+ end
+ end
end
diff_line.text = rich_line
diff --git a/lib/gitlab/encoding_helper.rb b/lib/gitlab/encoding_helper.rb
index c0edcabc6fd..6659efa0961 100644
--- a/lib/gitlab/encoding_helper.rb
+++ b/lib/gitlab/encoding_helper.rb
@@ -28,9 +28,9 @@ module Gitlab
# encode and clean the bad chars
message.replace clean(message)
- rescue ArgumentError
- return nil
- rescue
+ rescue ArgumentError => e
+ return unless e.message.include?('unknown encoding name')
+
encoding = detect ? detect[:encoding] : "unknown"
"--broken encoding: #{encoding}"
end
diff --git a/lib/gitlab/file_finder.rb b/lib/gitlab/file_finder.rb
index 10ffc345bd5..8c082c0c336 100644
--- a/lib/gitlab/file_finder.rb
+++ b/lib/gitlab/file_finder.rb
@@ -28,7 +28,7 @@ module Gitlab
def find_by_content(query)
results = repository.search_files_by_content(query, ref).first(BATCH_SIZE)
- results.map { |result| Gitlab::ProjectSearchResults.parse_search_result(result) }
+ results.map { |result| Gitlab::ProjectSearchResults.parse_search_result(result, project) }
end
def find_by_filename(query, except: [])
@@ -45,7 +45,8 @@ module Gitlab
basename: File.basename(blob.path),
ref: ref,
startline: 1,
- data: blob.data
+ data: blob.data,
+ project: project
)
end
end
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index d95561fe1b2..ae27a138b7c 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -508,7 +508,7 @@ module Gitlab
@committed_date = Time.at(commit.committer.date.seconds).utc
@committer_name = commit.committer.name.dup
@committer_email = commit.committer.email.dup
- @parent_ids = commit.parent_ids
+ @parent_ids = Array(commit.parent_ids)
end
def serialize_keys
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 6761fb0937a..ddb9cf433eb 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1234,7 +1234,13 @@ module Gitlab
end
def squash_in_progress?(squash_id)
- fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id))
+ gitaly_migrate(:squash_in_progress) do |is_enabled|
+ if is_enabled
+ gitaly_repository_client.squash_in_progress?(squash_id)
+ else
+ fresh_worktree?(worktree_path(SQUASH_WORKTREE_PREFIX, squash_id))
+ end
+ end
end
def push_remote_branches(remote_name, branch_names, forced: true)
@@ -1349,7 +1355,7 @@ module Gitlab
if is_enabled
gitaly_ref_client.branch_names_contains_sha(sha)
else
- refs_contains_sha(:branch, sha)
+ refs_contains_sha('refs/heads/', sha)
end
end
end
@@ -1359,7 +1365,7 @@ module Gitlab
if is_enabled
gitaly_ref_client.tag_names_contains_sha(sha)
else
- refs_contains_sha(:tag, sha)
+ refs_contains_sha('refs/tags/', sha)
end
end
end
@@ -1458,19 +1464,25 @@ module Gitlab
end
end
- def refs_contains_sha(ref_type, sha)
- args = %W(#{ref_type} --contains #{sha})
- names = run_git(args).first
+ def refs_contains_sha(refs_prefix, sha)
+ refs_prefix << "/" unless refs_prefix.ends_with?('/')
- return [] unless names.respond_to?(:split)
+ # By forcing the output to %(refname) each line wiht a ref will start with
+ # the ref prefix. All other lines can be discarded.
+ args = %W(for-each-ref --contains=#{sha} --format=%(refname) #{refs_prefix})
+ names, code = run_git(args)
- names = names.split("\n").map(&:strip)
+ return [] unless code.zero?
- names.each do |name|
- name.slice! '* '
+ refs = []
+ left_slice_count = refs_prefix.length
+ names.lines.each do |line|
+ next unless line.start_with?(refs_prefix)
+
+ refs << line.rstrip[left_slice_count..-1]
end
- names
+ refs
end
def rugged_write_config(full_path:)
@@ -1614,17 +1626,14 @@ module Gitlab
# Gitaly note: JV: Trying to get rid of the 'filter' option so we can implement this with 'git'.
def branches_filter(filter: nil, sort_by: nil)
- # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37464
- branches = Gitlab::GitalyClient.allow_n_plus_1_calls do
- rugged.branches.each(filter).map do |rugged_ref|
- begin
- target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
- Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
- rescue Rugged::ReferenceError
- # Omit invalid branch
- end
- end.compact
- end
+ branches = rugged.branches.each(filter).map do |rugged_ref|
+ begin
+ target_commit = Gitlab::Git::Commit.find(self, rugged_ref.target)
+ Gitlab::Git::Branch.new(self, rugged_ref.name, rugged_ref.target, target_commit)
+ rescue Rugged::ReferenceError
+ # Omit invalid branch
+ end
+ end.compact
sort_branches(branches, sort_by)
end
@@ -2191,13 +2200,14 @@ module Gitlab
)
diff_range = "#{start_sha}...#{end_sha}"
diff_files = run_git!(
- %W(diff --name-only --diff-filter=a --binary #{diff_range})
+ %W(diff --name-only --diff-filter=ar --binary #{diff_range})
).chomp
with_worktree(squash_path, branch, sparse_checkout_files: diff_files, env: env) do
# Apply diff of the `diff_range` to the worktree
diff = run_git!(%W(diff --binary #{diff_range}))
- run_git!(%w(apply --index), chdir: squash_path, env: env) do |stdin|
+ run_git!(%w(apply --index --whitespace=nowarn), chdir: squash_path, env: env) do |stdin|
+ stdin.binmode
stdin.write(diff)
end
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index ba6058fd3c9..b6ceb542dd1 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -14,14 +14,14 @@ module Gitlab
# Uses rugged for raw objects
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/320
- def where(repository, sha, path = nil)
+ def where(repository, sha, path = nil, recursive = false)
path = nil if path == '' || path == '/'
Gitlab::GitalyClient.migrate(:tree_entries) do |is_enabled|
if is_enabled
- repository.gitaly_commit_client.tree_entries(repository, sha, path)
+ repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive)
else
- tree_entries_from_rugged(repository, sha, path)
+ tree_entries_from_rugged(repository, sha, path, recursive)
end
end
end
@@ -57,7 +57,22 @@ module Gitlab
end
end
- def tree_entries_from_rugged(repository, sha, path)
+ 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
+
+ ordered_entries
+ end
+
+ def get_tree_entries_from_rugged(repository, sha, path)
commit = repository.lookup(sha)
root_tree = commit.tree
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index 8ec3386184a..6400089a22f 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -198,8 +198,8 @@ module Gitlab
end
def check_repository_existence!
- unless project.repository.exists?
- raise UnauthorizedError, ERROR_MESSAGES[:no_repo]
+ unless repository.exists?
+ raise NotFoundError, ERROR_MESSAGES[:no_repo]
end
end
@@ -238,19 +238,22 @@ module Gitlab
changes_list = Gitlab::ChangesList.new(changes)
# Iterate over all changes to find if user allowed all of them to be applied
- changes_list.each do |change|
+ changes_list.each.with_index do |change, index|
+ first_change = index == 0
+
# If user does not have access to make at least one change, cancel all
# push by allowing the exception to bubble up
- check_single_change_access(change)
+ check_single_change_access(change, skip_lfs_integrity_check: !first_change)
end
end
- def check_single_change_access(change)
+ def check_single_change_access(change, skip_lfs_integrity_check: false)
Checks::ChangeAccess.new(
change,
user_access: user_access,
project: project,
skip_authorization: deploy_key?,
+ skip_lfs_integrity_check: skip_lfs_integrity_check,
protocol: protocol
).exec
end
@@ -324,5 +327,9 @@ module Gitlab
def push_to_read_only_message
ERROR_MESSAGES[:cannot_push_to_read_only]
end
+
+ def repository
+ project.repository
+ end
end
end
diff --git a/lib/gitlab/git_access_wiki.rb b/lib/gitlab/git_access_wiki.rb
index 1c9477e84b2..a5b3902ebf4 100644
--- a/lib/gitlab/git_access_wiki.rb
+++ b/lib/gitlab/git_access_wiki.rb
@@ -13,7 +13,7 @@ module Gitlab
authentication_abilities.include?(:download_code) && user_access.can_do_action?(:download_wiki_code)
end
- def check_single_change_access(change)
+ def check_single_change_access(change, _options = {})
unless user_access.can_do_action?(:create_wiki)
raise UnauthorizedError, ERROR_MESSAGES[:write_to_wiki]
end
@@ -28,5 +28,11 @@ module Gitlab
def push_to_read_only_message
ERROR_MESSAGES[:read_only]
end
+
+ private
+
+ def repository
+ project.wiki.repository
+ end
end
end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index c5d3e944f7d..9cd76630484 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -125,6 +125,8 @@ 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
@@ -135,6 +137,27 @@ module Gitlab
duration)
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
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index 269a048cf5d..d60f57717b5 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -105,11 +105,12 @@ module Gitlab
entry unless entry.oid.blank?
end
- def tree_entries(repository, revision, path)
+ def tree_entries(repository, revision, path, recursive)
request = Gitaly::GetTreeEntriesRequest.new(
repository: @gitaly_repo,
revision: encode_binary(revision),
- path: path.present? ? encode_binary(path) : '.'
+ path: path.present? ? encode_binary(path) : '.',
+ recursive: recursive
)
response = GitalyClient.call(@repository.storage, :commit_service, :get_tree_entries, request, timeout: GitalyClient.medium_timeout)
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 60706b4f0d8..603457d0664 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -134,6 +134,23 @@ module Gitlab
response.in_progress
end
+ def squash_in_progress?(squash_id)
+ request = Gitaly::IsSquashInProgressRequest.new(
+ repository: @gitaly_repo,
+ squash_id: squash_id.to_s
+ )
+
+ response = GitalyClient.call(
+ @storage,
+ :repository_service,
+ :is_squash_in_progress,
+ request,
+ timeout: GitalyClient.default_timeout
+ )
+
+ response.in_progress
+ end
+
def fetch_source_branch(source_repository, source_branch, local_ref)
request = Gitaly::FetchSourceBranchRequest.new(
repository: @gitaly_repo,
diff --git a/lib/gitlab/github_import/importer/repository_importer.rb b/lib/gitlab/github_import/importer/repository_importer.rb
index 7dd68a0d1cd..ab0b751fe24 100644
--- a/lib/gitlab/github_import/importer/repository_importer.rb
+++ b/lib/gitlab/github_import/importer/repository_importer.rb
@@ -63,6 +63,7 @@ module Gitlab
true
rescue Gitlab::Shell::Error => e
if e.message !~ /repository not exported/
+ project.create_wiki
fail_import("Failed to import the wiki: #{e.message}")
else
true
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 86a90d57d9c..ba04387022d 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -13,8 +13,6 @@ module Gitlab
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.shortcuts_path = help_page_path('shortcuts')
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
- gon.katex_css_url = ActionController::Base.helpers.asset_path('katex.css')
- gon.katex_js_url = ActionController::Base.helpers.asset_path('katex.js')
gon.sentry_dsn = Gitlab::CurrentSettings.clientside_sentry_dsn if Gitlab::CurrentSettings.clientside_sentry_enabled
gon.gitlab_url = Gitlab.config.gitlab.url
gon.revision = Gitlab::REVISION
diff --git a/lib/gitlab/gpg/commit.rb b/lib/gitlab/gpg/commit.rb
index 672b5579dfd..90dd569aaf8 100644
--- a/lib/gitlab/gpg/commit.rb
+++ b/lib/gitlab/gpg/commit.rb
@@ -60,7 +60,9 @@ module Gitlab
def create_cached_signature!
using_keychain do |gpg_key|
- GpgSignature.create!(attributes(gpg_key))
+ signature = GpgSignature.new(attributes(gpg_key))
+ signature.save! unless Gitlab::Database.read_only?
+ signature
end
end
diff --git a/lib/gitlab/import_export/importer.rb b/lib/gitlab/import_export/importer.rb
index c14646b0611..a00795f553e 100644
--- a/lib/gitlab/import_export/importer.rb
+++ b/lib/gitlab/import_export/importer.rb
@@ -50,9 +50,10 @@ module Gitlab
end
def wiki_restorer
- Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path,
+ Gitlab::ImportExport::WikiRestorer.new(path_to_bundle: wiki_repo_path,
shared: @shared,
- project: ProjectWiki.new(project_tree.restored_project))
+ project: ProjectWiki.new(project_tree.restored_project),
+ wiki_enabled: @project.wiki_enabled?)
end
def uploads_restorer
diff --git a/lib/gitlab/import_export/project_creator.rb b/lib/gitlab/import_export/project_creator.rb
deleted file mode 100644
index 77bb3ca6581..00000000000
--- a/lib/gitlab/import_export/project_creator.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Gitlab
- module ImportExport
- class ProjectCreator
- def initialize(namespace_id, current_user, file, project_path)
- @namespace_id = namespace_id
- @current_user = current_user
- @file = file
- @project_path = project_path
- end
-
- def execute
- ::Projects::CreateService.new(
- @current_user,
- name: @project_path,
- path: @project_path,
- namespace_id: @namespace_id,
- import_type: "gitlab_project",
- import_source: @file
- ).execute
- end
- end
- end
-end
diff --git a/lib/gitlab/import_export/wiki_restorer.rb b/lib/gitlab/import_export/wiki_restorer.rb
new file mode 100644
index 00000000000..f33bfb332ab
--- /dev/null
+++ b/lib/gitlab/import_export/wiki_restorer.rb
@@ -0,0 +1,23 @@
+module Gitlab
+ module ImportExport
+ class WikiRestorer < RepoRestorer
+ def initialize(project:, shared:, path_to_bundle:, wiki_enabled:)
+ super(project: project, shared: shared, path_to_bundle: path_to_bundle)
+
+ @wiki_enabled = wiki_enabled
+ end
+
+ def restore
+ @project.wiki if create_empty_wiki?
+
+ super
+ end
+
+ private
+
+ def create_empty_wiki?
+ !File.exist?(@path_to_bundle) && @wiki_enabled
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/job_waiter.rb b/lib/gitlab/job_waiter.rb
index f654508c391..f7a8eae0be4 100644
--- a/lib/gitlab/job_waiter.rb
+++ b/lib/gitlab/job_waiter.rb
@@ -15,16 +15,22 @@ module Gitlab
# push to that array when done. Once the waiter has popped `count` items, it
# knows all the jobs are done.
class JobWaiter
+ KEY_PREFIX = "gitlab:job_waiter".freeze
+
def self.notify(key, jid)
Gitlab::Redis::SharedState.with { |redis| redis.lpush(key, jid) }
end
+ def self.key?(key)
+ key.is_a?(String) && key =~ /\A#{KEY_PREFIX}:\h{8}-\h{4}-\h{4}-\h{4}-\h{12}\z/
+ end
+
attr_reader :key, :finished
attr_accessor :jobs_remaining
# jobs_remaining - the number of jobs left to wait for
# key - The key of this waiter.
- def initialize(jobs_remaining = 0, key = "gitlab:job_waiter:#{SecureRandom.uuid}")
+ def initialize(jobs_remaining = 0, key = "#{KEY_PREFIX}:#{SecureRandom.uuid}")
@key = key
@jobs_remaining = jobs_remaining
@finished = []
diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb
index 47b3fce3e7a..a6bea98d631 100644
--- a/lib/gitlab/ldap/config.rb
+++ b/lib/gitlab/ldap/config.rb
@@ -15,7 +15,7 @@ module Gitlab
end
def self.servers
- Gitlab.config.ldap.servers.values
+ Gitlab.config.ldap['servers']&.values || []
end
def self.available_servers
diff --git a/lib/gitlab/ldap/person.rb b/lib/gitlab/ldap/person.rb
index b91757c2a4b..c59df556247 100644
--- a/lib/gitlab/ldap/person.rb
+++ b/lib/gitlab/ldap/person.rb
@@ -63,8 +63,6 @@ module Gitlab
Rails.logger.debug { "Instantiating #{self.class.name} with LDIF:\n#{entry.to_ldif}" }
@entry = entry
@provider = provider
-
- validate_entry
end
def name
@@ -117,19 +115,6 @@ module Gitlab
entry.public_send(selected_attr) # rubocop:disable GitlabSecurity/PublicSend
end
-
- def validate_entry
- allowed_attrs = self.class.ldap_attributes(config).map(&:downcase)
-
- # Net::LDAP::Entry transforms keys to symbols. Change to strings to compare.
- entry_attrs = entry.attribute_names.map { |n| n.to_s.downcase }
- invalid_attrs = entry_attrs - allowed_attrs
-
- if invalid_attrs.any?
- raise InvalidEntryError,
- "#{self.class.name} initialized with Net::LDAP::Entry containing invalid attributes(s): #{invalid_attrs}"
- end
- end
end
end
end
diff --git a/lib/gitlab/metrics/sidekiq_metrics_exporter.rb b/lib/gitlab/metrics/sidekiq_metrics_exporter.rb
index 5980a4ded2b..db8bdde74b2 100644
--- a/lib/gitlab/metrics/sidekiq_metrics_exporter.rb
+++ b/lib/gitlab/metrics/sidekiq_metrics_exporter.rb
@@ -23,7 +23,7 @@ module Gitlab
end
def stop_working
- server.shutdown
+ server.shutdown if server
@server = nil
end
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index 45b9e14ba55..f3e48083c19 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -73,7 +73,7 @@ module Gitlab
# event_name - The name of the event (e.g. "git_push").
# tags - A set of tags to attach to the event.
def add_event(event_name, tags = {})
- self.class.transaction_metric(event_name, :counter, prefix: 'event_', tags: tags).increment(tags.merge(labels))
+ self.class.transaction_metric(event_name, :counter, prefix: 'event_', use_feature_flag: true, tags: tags).increment(tags.merge(labels))
@metrics << Metric.new(EVENT_SERIES, { count: 1 }, tags.merge(event: event_name), :event)
end
@@ -150,11 +150,12 @@ module Gitlab
with_feature :prometheus_metrics_transaction_allocated_memory
end
- def self.transaction_metric(name, type, prefix: nil, tags: {})
+ def self.transaction_metric(name, type, prefix: nil, use_feature_flag: false, tags: {})
metric_name = "gitlab_transaction_#{prefix}#{name}_total".to_sym
fetch_metric(type, metric_name) do
docstring "Transaction #{prefix}#{name} #{type}"
base_labels tags.merge(BASE_LABELS)
+ with_feature "prometheus_transaction_#{prefix}#{name}_total".to_sym if use_feature_flag
if type == :gauge
multiprocess_mode :livesum
diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb
index 1a570f480c6..1fd8f147b44 100644
--- a/lib/gitlab/middleware/go.rb
+++ b/lib/gitlab/middleware/go.rb
@@ -114,7 +114,15 @@ module Gitlab
end
def current_user(request)
- request.env['warden']&.authenticate
+ authenticator = Gitlab::Auth::RequestAuthenticator.new(request)
+ user = authenticator.find_user_from_access_token || authenticator.find_user_from_warden
+
+ return unless user&.can?(:access_api)
+
+ # Right now, the `api` scope is the only one that should be able to determine private project existence.
+ return unless authenticator.valid_access_token?(scopes: [:api])
+
+ user
end
end
end
diff --git a/lib/gitlab/middleware/multipart.rb b/lib/gitlab/middleware/multipart.rb
index cc1e92480be..d4c54049b74 100644
--- a/lib/gitlab/middleware/multipart.rb
+++ b/lib/gitlab/middleware/multipart.rb
@@ -42,7 +42,7 @@ module Gitlab
key, value = parsed_field.first
if value.nil?
- value = open_file(tmp_path)
+ value = open_file(tmp_path, @request.params["#{key}.name"])
@open_files << value
else
value = decorate_params_value(value, @request.params[key], tmp_path)
@@ -70,7 +70,7 @@ module Gitlab
case path_value
when nil
- value_hash[path_key] = open_file(tmp_path)
+ value_hash[path_key] = open_file(tmp_path, value_hash.dig(path_key, '.name'))
@open_files << value_hash[path_key]
value_hash
when Hash
@@ -81,8 +81,8 @@ module Gitlab
end
end
- def open_file(path)
- ::UploadedFile.new(path, File.basename(path), 'application/octet-stream')
+ def open_file(path, name)
+ ::UploadedFile.new(path, name || File.basename(path), 'application/octet-stream')
end
end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index a3e1c66c19f..28ebac1776e 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -198,9 +198,11 @@ module Gitlab
end
def update_profile
+ clear_user_synced_attributes_metadata
+
return unless sync_profile_from_provider? || creating_linked_ldap_user?
- metadata = gl_user.user_synced_attributes_metadata || gl_user.build_user_synced_attributes_metadata
+ metadata = gl_user.build_user_synced_attributes_metadata
if sync_profile_from_provider?
UserSyncedAttributesMetadata::SYNCABLE_ATTRIBUTES.each do |key|
@@ -221,6 +223,10 @@ module Gitlab
end
end
+ def clear_user_synced_attributes_metadata
+ gl_user&.user_synced_attributes_metadata&.destroy
+ end
+
def log
Gitlab::AppLogger
end
diff --git a/lib/gitlab/profiler.rb b/lib/gitlab/profiler.rb
index 95d94b3cc68..98a168b43bb 100644
--- a/lib/gitlab/profiler.rb
+++ b/lib/gitlab/profiler.rb
@@ -45,6 +45,7 @@ module Gitlab
if user
private_token ||= user.personal_access_tokens.active.pluck(:token).first
+ raise 'Your user must have a personal_access_token' unless private_token
end
headers['Private-Token'] = private_token if private_token
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index 9e2fa07a205..cf0935dbd9a 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -41,7 +41,7 @@ module Gitlab
@commits_count ||= commits.count
end
- def self.parse_search_result(result)
+ def self.parse_search_result(result, project = nil)
ref = nil
filename = nil
basename = nil
@@ -66,7 +66,8 @@ module Gitlab
basename: basename,
ref: ref,
startline: startline,
- data: data
+ data: data,
+ project_id: project ? project.id : nil
)
end
diff --git a/lib/gitlab/prometheus/metric_group.rb b/lib/gitlab/prometheus/metric_group.rb
index 729fef34b35..e91c6fb2e27 100644
--- a/lib/gitlab/prometheus/metric_group.rb
+++ b/lib/gitlab/prometheus/metric_group.rb
@@ -6,9 +6,14 @@ module Gitlab
attr_accessor :name, :priority, :metrics
validates :name, :priority, :metrics, presence: true
- def self.all
+ def self.common_metrics
AdditionalMetricsParser.load_groups_from_yaml
end
+
+ # EE only
+ def self.for_project(_)
+ common_metrics
+ end
end
end
end
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
index 294a6ae34ca..972ab75d1d5 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_deployment_query.rb
@@ -7,6 +7,7 @@ module Gitlab
def query(environment_id, deployment_id)
Deployment.find_by(id: deployment_id).try do |deployment|
query_metrics(
+ deployment.project,
common_query_context(
deployment.environment,
timeframe_start: (deployment.created_at - 30.minutes).to_f,
diff --git a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
index 32fe8201a8d..9273e69e158 100644
--- a/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
+++ b/lib/gitlab/prometheus/queries/additional_metrics_environment_query.rb
@@ -7,6 +7,7 @@ module Gitlab
def query(environment_id)
::Environment.find_by(id: environment_id).try do |environment|
query_metrics(
+ environment.project,
common_query_context(environment, timeframe_start: 8.hours.ago.to_f, timeframe_end: Time.now.to_f)
)
end
diff --git a/lib/gitlab/prometheus/queries/matched_metrics_query.rb b/lib/gitlab/prometheus/queries/matched_metrics_query.rb
index 4c3edccc71a..5710ad47c1a 100644
--- a/lib/gitlab/prometheus/queries/matched_metrics_query.rb
+++ b/lib/gitlab/prometheus/queries/matched_metrics_query.rb
@@ -18,7 +18,7 @@ module Gitlab
private
def groups_data
- metrics_groups = groups_with_active_metrics(Gitlab::Prometheus::MetricGroup.all)
+ metrics_groups = groups_with_active_metrics(Gitlab::Prometheus::MetricGroup.common_metrics)
lookup = active_series_lookup(metrics_groups)
groups = {}
diff --git a/lib/gitlab/prometheus/queries/query_additional_metrics.rb b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
index 5cddc96a643..0c280dc9a3c 100644
--- a/lib/gitlab/prometheus/queries/query_additional_metrics.rb
+++ b/lib/gitlab/prometheus/queries/query_additional_metrics.rb
@@ -2,10 +2,10 @@ module Gitlab
module Prometheus
module Queries
module QueryAdditionalMetrics
- def query_metrics(query_context)
+ def query_metrics(project, query_context)
query_processor = method(:process_query).curry[query_context]
- groups = matched_metrics.map do |group|
+ groups = matched_metrics(project).map do |group|
metrics = group.metrics.map do |metric|
{
title: metric.title,
@@ -60,8 +60,8 @@ module Gitlab
@available_metrics ||= client_label_values || []
end
- def matched_metrics
- result = Gitlab::Prometheus::MetricGroup.all.map do |group|
+ def matched_metrics(project)
+ result = Gitlab::Prometheus::MetricGroup.for_project(project).map do |group|
group.metrics.select! do |metric|
metric.required_metrics.all?(&available_metrics.method(:include?))
end
diff --git a/lib/gitlab/prometheus_client.rb b/lib/gitlab/prometheus_client.rb
index 10527972663..659021c9ac9 100644
--- a/lib/gitlab/prometheus_client.rb
+++ b/lib/gitlab/prometheus_client.rb
@@ -1,8 +1,9 @@
module Gitlab
- PrometheusError = Class.new(StandardError)
-
# Helper methods to interact with Prometheus network services & resources
class PrometheusClient
+ Error = Class.new(StandardError)
+ QueryError = Class.new(Gitlab::PrometheusClient::Error)
+
attr_reader :rest_client, :headers
def initialize(rest_client)
@@ -22,10 +23,10 @@ module Gitlab
def query_range(query, start: 8.hours.ago, stop: Time.now)
get_result('matrix') do
json_api_get('query_range',
- query: query,
- start: start.to_f,
- end: stop.to_f,
- step: 1.minute.to_i)
+ query: query,
+ start: start.to_f,
+ end: stop.to_f,
+ step: 1.minute.to_i)
end
end
@@ -43,22 +44,22 @@ module Gitlab
path = ['api', 'v1', type].join('/')
get(path, args)
rescue JSON::ParserError
- raise PrometheusError, 'Parsing response failed'
+ raise PrometheusClient::Error, 'Parsing response failed'
rescue Errno::ECONNREFUSED
- raise PrometheusError, 'Connection refused'
+ raise PrometheusClient::Error, 'Connection refused'
end
def get(path, args)
response = rest_client[path].get(params: args)
handle_response(response)
rescue SocketError
- raise PrometheusError, "Can't connect to #{rest_client.url}"
+ raise PrometheusClient::Error, "Can't connect to #{rest_client.url}"
rescue OpenSSL::SSL::SSLError
- raise PrometheusError, "#{rest_client.url} contains invalid SSL data"
+ raise PrometheusClient::Error, "#{rest_client.url} contains invalid SSL data"
rescue RestClient::ExceptionWithResponse => ex
handle_exception_response(ex.response)
rescue RestClient::Exception
- raise PrometheusError, "Network connection error"
+ raise PrometheusClient::Error, "Network connection error"
end
def handle_response(response)
@@ -66,16 +67,18 @@ module Gitlab
if response.code == 200 && json_data['status'] == 'success'
json_data['data'] || {}
else
- raise PrometheusError, "#{response.code} - #{response.body}"
+ raise PrometheusClient::Error, "#{response.code} - #{response.body}"
end
end
def handle_exception_response(response)
- if response.code == 400
+ if response.code == 200 && response['status'] == 'success'
+ response['data'] || {}
+ elsif response.code == 400
json_data = JSON.parse(response.body)
- raise PrometheusError, json_data['error'] || 'Bad data received'
+ raise PrometheusClient::QueryError, json_data['error'] || 'Bad data received'
else
- raise PrometheusError, "#{response.code} - #{response.body}"
+ raise PrometheusClient::Error, "#{response.code} - #{response.body}"
end
end
diff --git a/lib/gitlab/query_limiting.rb b/lib/gitlab/query_limiting.rb
index f64f1757144..9f69a9e4a39 100644
--- a/lib/gitlab/query_limiting.rb
+++ b/lib/gitlab/query_limiting.rb
@@ -6,7 +6,7 @@ module Gitlab
# This ensures we don't produce any errors that users can't do anything
# about themselves.
def self.enable?
- Gitlab.com? || Rails.env.development? || Rails.env.test?
+ Rails.env.development? || Rails.env.test?
end
# Allows the current request to execute any number of SQL queries.
diff --git a/lib/gitlab/query_limiting/active_support_subscriber.rb b/lib/gitlab/query_limiting/active_support_subscriber.rb
index 66049c94ec6..4c83581c4b1 100644
--- a/lib/gitlab/query_limiting/active_support_subscriber.rb
+++ b/lib/gitlab/query_limiting/active_support_subscriber.rb
@@ -3,8 +3,10 @@ module Gitlab
class ActiveSupportSubscriber < ActiveSupport::Subscriber
attach_to :active_record
- def sql(*)
- Transaction.current&.increment
+ def sql(event)
+ unless event.payload[:name] == 'CACHE'
+ Transaction.current&.increment
+ end
end
end
end
diff --git a/lib/gitlab/query_limiting/transaction.rb b/lib/gitlab/query_limiting/transaction.rb
index 3cbafadb0d0..66d7d9275cf 100644
--- a/lib/gitlab/query_limiting/transaction.rb
+++ b/lib/gitlab/query_limiting/transaction.rb
@@ -51,13 +51,7 @@ module Gitlab
error = ThresholdExceededError.new(error_message)
- if raise_error?
- raise(error)
- else
- # Raven automatically logs to the Rails log if disabled, thus we don't
- # need to manually log anything in case Sentry support is not enabled.
- Raven.capture_exception(error)
- end
+ raise(error) if raise_error?
end
def increment
diff --git a/lib/gitlab/quick_actions/command_definition.rb b/lib/gitlab/quick_actions/command_definition.rb
index 3937d9c153a..96415271316 100644
--- a/lib/gitlab/quick_actions/command_definition.rb
+++ b/lib/gitlab/quick_actions/command_definition.rb
@@ -24,15 +24,14 @@ module Gitlab
action_block.nil?
end
- def available?(opts)
+ def available?(context)
return true unless condition_block
- context = OpenStruct.new(opts)
context.instance_exec(&condition_block)
end
- def explain(context, opts, arg)
- return unless available?(opts)
+ def explain(context, arg)
+ return unless available?(context)
if explanation.respond_to?(:call)
execute_block(explanation, context, arg)
@@ -41,15 +40,13 @@ module Gitlab
end
end
- def execute(context, opts, arg)
- return if noop? || !available?(opts)
+ def execute(context, arg)
+ return if noop? || !available?(context)
execute_block(action_block, context, arg)
end
- def to_h(opts)
- context = OpenStruct.new(opts)
-
+ def to_h(context)
desc = description
if desc.respond_to?(:call)
desc = context.instance_exec(&desc) rescue ''
diff --git a/lib/gitlab/quick_actions/dsl.rb b/lib/gitlab/quick_actions/dsl.rb
index 536765305e1..d82dccd0db5 100644
--- a/lib/gitlab/quick_actions/dsl.rb
+++ b/lib/gitlab/quick_actions/dsl.rb
@@ -62,9 +62,8 @@ module Gitlab
# Allows to define conditions that must be met in order for the command
# to be returned by `.command_names` & `.command_definitions`.
- # It accepts a block that will be evaluated with the context given to
- # `CommandDefintion#to_h`.
- #
+ # It accepts a block that will be evaluated with the context
+ # of a QuickActions::InterpretService instance
# Example:
#
# condition do
diff --git a/lib/gitlab/quick_actions/extractor.rb b/lib/gitlab/quick_actions/extractor.rb
index c0878a34fb1..075ff91700c 100644
--- a/lib/gitlab/quick_actions/extractor.rb
+++ b/lib/gitlab/quick_actions/extractor.rb
@@ -29,7 +29,7 @@ module Gitlab
# commands = extractor.extract_commands(msg) #=> [['labels', '~foo ~"bar baz"']]
# msg #=> "hello\nworld"
# ```
- def extract_commands(content, opts = {})
+ def extract_commands(content)
return [content, []] unless content
content = content.dup
@@ -37,7 +37,7 @@ module Gitlab
commands = []
content.delete!("\r")
- content.gsub!(commands_regex(opts)) do
+ content.gsub!(commands_regex) do
if $~[:cmd]
commands << [$~[:cmd], $~[:arg]].reject(&:blank?)
''
@@ -60,8 +60,8 @@ module Gitlab
# It looks something like:
#
# /^\/(?<cmd>close|reopen|...)(?:( |$))(?<arg>[^\/\n]*)(?:\n|$)/
- def commands_regex(opts)
- names = command_names(opts).map(&:to_s)
+ def commands_regex
+ names = command_names.map(&:to_s)
@commands_regex ||= %r{
(?<code>
@@ -133,7 +133,7 @@ module Gitlab
[content, commands]
end
- def command_names(opts)
+ def command_names
command_definitions.flat_map do |command|
next if command.noop?
diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb
index 7ab85e1c35c..ac3de2a8f71 100644
--- a/lib/gitlab/regex.rb
+++ b/lib/gitlab/regex.rb
@@ -40,12 +40,16 @@ module Gitlab
'a-zA-Z0-9_/\\$\\{\\}\\. \\-'
end
+ def environment_name_regex_chars_without_slash
+ 'a-zA-Z0-9_\\$\\{\\}\\. -'
+ end
+
def environment_name_regex
- @environment_name_regex ||= /\A[#{environment_name_regex_chars}]+\z/.freeze
+ @environment_name_regex ||= /\A[#{environment_name_regex_chars_without_slash}]([#{environment_name_regex_chars}]*[#{environment_name_regex_chars_without_slash}])?\z/.freeze
end
def environment_name_regex_message
- "can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.', and spaces"
+ "can contain only letters, digits, '-', '_', '/', '$', '{', '}', '.', and spaces, but it cannot start or end with '/'"
end
def kubernetes_namespace_regex
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 5ad219179f3..5a5ae7f19d4 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -1,7 +1,7 @@
module Gitlab
class SearchResults
class FoundBlob
- attr_reader :id, :filename, :basename, :ref, :startline, :data
+ attr_reader :id, :filename, :basename, :ref, :startline, :data, :project_id
def initialize(opts = {})
@id = opts.fetch(:id, nil)
@@ -11,6 +11,7 @@ module Gitlab
@startline = opts.fetch(:startline, nil)
@data = opts.fetch(:data, nil)
@per_page = opts.fetch(:per_page, 20)
+ @project_id = opts.fetch(:project_id, nil)
end
def path
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index e90a90508a2..07d7c91cb5d 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -37,7 +37,7 @@ module Gitlab
config[:'gitlab-shell'] = { dir: Gitlab.config.gitlab_shell.path }
config[:bin_dir] = Gitlab.config.gitaly.client_path
- TOML.dump(config)
+ TomlRB.dump(config)
end
# rubocop:disable Rails/Output
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index f4a41dc3eda..4ba44e0feef 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -294,7 +294,8 @@ module Gitlab
# add_namespace("/path/to/storage", "gitlab")
#
def add_namespace(storage, name)
- Gitlab::GitalyClient.migrate(:add_namespace) do |enabled|
+ Gitlab::GitalyClient.migrate(:add_namespace,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled
gitaly_namespace_client(storage).add(name)
else
@@ -315,7 +316,8 @@ module Gitlab
# rm_namespace("/path/to/storage", "gitlab")
#
def rm_namespace(storage, name)
- Gitlab::GitalyClient.migrate(:remove_namespace) do |enabled|
+ Gitlab::GitalyClient.migrate(:remove_namespace,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled
gitaly_namespace_client(storage).remove(name)
else
@@ -333,7 +335,8 @@ module Gitlab
# mv_namespace("/path/to/storage", "gitlab", "gitlabhq")
#
def mv_namespace(storage, old_name, new_name)
- Gitlab::GitalyClient.migrate(:rename_namespace) do |enabled|
+ Gitlab::GitalyClient.migrate(:rename_namespace,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled
gitaly_namespace_client(storage).rename(old_name, new_name)
else
@@ -368,7 +371,8 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/385
def exists?(storage, dir_name)
- Gitlab::GitalyClient.migrate(:namespace_exists) do |enabled|
+ Gitlab::GitalyClient.migrate(:namespace_exists,
+ status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled|
if enabled
gitaly_namespace_client(storage).exists?(dir_name)
else
diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb
deleted file mode 100644
index b89ae2505c9..00000000000
--- a/lib/gitlab/sidekiq_middleware/memory_killer.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-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
- return 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, 'SIGSTP', '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
- 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
-
- 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
new file mode 100644
index 00000000000..c2b8d6de66e
--- /dev/null
+++ b/lib/gitlab/sidekiq_middleware/shutdown.rb
@@ -0,0 +1,133 @@
+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
+ return 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/slash_commands/base_command.rb b/lib/gitlab/slash_commands/base_command.rb
index cc3c9a50555..466554e398c 100644
--- a/lib/gitlab/slash_commands/base_command.rb
+++ b/lib/gitlab/slash_commands/base_command.rb
@@ -31,10 +31,11 @@ module Gitlab
raise NotImplementedError
end
- attr_accessor :project, :current_user, :params
+ attr_accessor :project, :current_user, :params, :chat_name
- def initialize(project, user, params = {})
- @project, @current_user, @params = project, user, params.dup
+ def initialize(project, chat_name, params = {})
+ @project, @current_user, @params = project, chat_name.user, params.dup
+ @chat_name = chat_name
end
private
diff --git a/lib/gitlab/slash_commands/command.rb b/lib/gitlab/slash_commands/command.rb
index a78408b0519..85aaa6b0eba 100644
--- a/lib/gitlab/slash_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -13,12 +13,13 @@ module Gitlab
if command
if command.allowed?(project, current_user)
- command.new(project, current_user, params).execute(match)
+ command.new(project, chat_name, params).execute(match)
else
Gitlab::SlashCommands::Presenters::Access.new.access_denied
end
else
- Gitlab::SlashCommands::Help.new(project, current_user, params).execute(available_commands, params[:text])
+ Gitlab::SlashCommands::Help.new(project, chat_name, params)
+ .execute(available_commands, params[:text])
end
end
diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb
index 5f0c98cb5a4..53744bad1f4 100644
--- a/lib/gitlab/sql/pattern.rb
+++ b/lib/gitlab/sql/pattern.rb
@@ -25,7 +25,11 @@ module Gitlab
query.length >= MIN_CHARS_FOR_PARTIAL_MATCHING
end
- def fuzzy_arel_match(column, query)
+ # column - The column name to search in.
+ # query - The text to search for.
+ # lower_exact_match - When set to `true` we'll fall back to using
+ # `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?
@@ -36,7 +40,13 @@ module Gitlab
else
# No words of at least 3 chars, but we can search for an exact
# case insensitive match with the query as a whole
- arel_table[column].matches(sanitize_sql_like(query))
+ if lower_exact_match
+ Arel::Nodes::NamedFunction
+ .new('LOWER', [arel_table[column]])
+ .eq(query)
+ else
+ arel_table[column].matches(sanitize_sql_like(query))
+ end
end
end
diff --git a/lib/gitlab/ssh_public_key.rb b/lib/gitlab/ssh_public_key.rb
index 545e7c74f7e..6f63ea91ae8 100644
--- a/lib/gitlab/ssh_public_key.rb
+++ b/lib/gitlab/ssh_public_key.rb
@@ -53,7 +53,7 @@ module Gitlab
end
def valid?
- key.present? && bits && technology.supported_sizes.include?(bits)
+ SSHKey.valid_ssh_public_key?(key_text)
end
def type
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 15eb1c41213..ff4dc29efea 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -65,7 +65,7 @@ module Gitlab
return false unless can_access_git?
if protected?(ProtectedBranch, project, ref)
- return true if project.empty_repo? && project.user_can_push_to_empty_repo?(user)
+ return true if project.user_can_push_to_empty_repo?(user)
protected_branch_accessible_to?(ref, action: :push)
else
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index ff638c07755..f30dd995695 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -76,9 +76,13 @@ module GoogleApi
"initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type
+ },
+ "legacy_abac": {
+ "enabled": true
}
}
- } )
+ }
+ )
service.create_cluster(project_id, zone, request_body, options: user_agent_header)
end
diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake
index c2d3a6b6950..c6942d22926 100644
--- a/lib/tasks/gemojione.rake
+++ b/lib/tasks/gemojione.rake
@@ -115,7 +115,7 @@ namespace :gemojione do
end
end
- style_path = Rails.root.join(*%w(app assets stylesheets framework emoji-sprites.scss))
+ style_path = Rails.root.join(*%w(app assets stylesheets framework emoji_sprites.scss))
# Combine the resized assets into a packed sprite and re-generate the SCSS
SpriteFactory.cssurl = "image-url('$IMAGE')"
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index 107ff1d8aeb..e9ca6404fe8 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -2,7 +2,7 @@ namespace :gitlab do
namespace :gitaly do
desc "GitLab | Install or upgrade gitaly"
task :install, [:dir, :repo] => :gitlab_environment do |t, args|
- require 'toml'
+ require 'toml-rb'
warn_user_is_not_gitlab
@@ -38,7 +38,7 @@ namespace :gitlab do
desc "GitLab | Print storage configuration in TOML format"
task storage_config: :environment do
- require 'toml'
+ require 'toml-rb'
puts "# Gitaly storage configuration generated from #{Gitlab.config.source} on #{Time.current.to_s(:long)}"
puts "# This is in TOML format suitable for use in Gitaly's config.toml file."
diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake
index 3ab406eff2c..fe5032cae18 100644
--- a/lib/tasks/lint.rake
+++ b/lib/tasks/lint.rake
@@ -16,5 +16,54 @@ unless Rails.env.production?
task :javascript do
Rake::Task['eslint'].invoke
end
+
+ desc "GitLab | lint | Run several lint checks"
+ task :all do
+ status = 0
+
+ %w[
+ config_lint
+ haml_lint
+ scss_lint
+ flay
+ gettext:lint
+ lint:static_verification
+ ].each do |task|
+ pid = Process.fork do
+ rd, wr = IO.pipe
+ stdout = $stdout.dup
+ stderr = $stderr.dup
+ $stdout.reopen(wr)
+ $stderr.reopen(wr)
+
+ begin
+ begin
+ Rake::Task[task].invoke
+ rescue RuntimeError # The haml_lint tasks raise a RuntimeError
+ exit(1)
+ end
+ rescue SystemExit => ex
+ msg = "*** Rake task #{task} failed with the following error(s):"
+ raise ex
+ ensure
+ $stdout.reopen(stdout)
+ $stderr.reopen(stderr)
+ wr.close
+
+ if msg
+ warn "\n#{msg}\n\n"
+ IO.copy_stream(rd, $stderr)
+ else
+ IO.copy_stream(rd, $stdout)
+ end
+ end
+ end
+
+ Process.waitpid(pid)
+ status += $?.exitstatus
+ end
+
+ exit(status)
+ end
end
end
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index 31cbd651edb..1c7a8a90f5c 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -8,6 +8,7 @@ task setup_postgresql: :environment do
require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like')
require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb')
require Rails.root.join('db/migrate/20180113220114_rework_redirect_routes_indexes.rb')
+ require Rails.root.join('db/migrate/20180215181245_users_name_lower_index.rb')
NamespacesProjectsPathLowerIndexes.new.up
AddUsersLowerUsernameEmailIndexes.new.up
@@ -17,4 +18,5 @@ task setup_postgresql: :environment do
IndexRedirectRoutesPathForLike.new.up
AddIndexOnNamespacesLowerName.new.up
ReworkRedirectRoutesIndexes.new.up
+ UsersNameLowerIndex.new.up
end
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index 81ce8ec506e..badd665c08c 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:58-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Bulgarian\n"
"Language: bg_BG\n"
@@ -16,23 +16,41 @@ msgstr ""
"X-Crowdin-Language: bg\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d подаване"
msgstr[1] "%d подаваниÑ"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
msgstr[1] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s подаване беше пропуÑнато, за да не Ñе натоварва ÑиÑтемата."
msgstr[1] "%s Ð¿Ð¾Ð´Ð°Ð²Ð°Ð½Ð¸Ñ Ð±Ñха пропуÑнати, за да не Ñе натоварва ÑиÑтемата."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} подаде %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -45,9 +63,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
@@ -121,24 +136,81 @@ msgstr "ДобавÑне на лиценз"
msgid "Add new directory"
msgstr "ДобавÑне на нова папка"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr ""
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -163,9 +235,6 @@ msgstr "ÐаиÑтина ли иÑкате да изтриете този пла
msgid "Are you sure you want to discard your changes?"
msgstr ""
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -178,6 +247,21 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Прикачете файл чрез влачене и пуÑкане или %{upload_link}"
@@ -193,13 +277,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -223,6 +310,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -277,6 +370,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Клон"
@@ -405,8 +501,8 @@ msgstr "от"
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
-msgstr "ÐšÐ¾Ð½Ñ„Ð¸Ð³ÑƒÑ€Ð°Ñ†Ð¸Ñ Ð½Ð° непрекъÑната интеграциÑ"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -417,6 +513,9 @@ msgstr "Отказ"
msgid "Cancel edit"
msgstr ""
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -432,15 +531,24 @@ msgstr "Подбиране"
msgid "ChangeTypeAction|Revert"
msgstr "ОтмÑна"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "СпиÑък Ñ Ð¿Ñ€Ð¾Ð¼ÐµÐ½Ð¸"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Графики"
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -453,7 +561,19 @@ msgstr "Подбиране на това подаване"
msgid "Cherry-pick this merge request"
msgstr "Подбиране на тази заÑвка за Ñливане"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -510,79 +630,91 @@ msgstr "пропуÑнато"
msgid "CiStatus|running"
msgstr "протича в момента"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -591,37 +723,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -639,64 +768,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -708,16 +867,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,7 +888,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -747,25 +909,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -777,7 +939,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -795,6 +957,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -812,6 +977,9 @@ msgstr "Времетраене на подаваниÑта в минути за
msgid "Commit message"
msgstr "Съобщение за подаването"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Подаване"
@@ -824,15 +992,57 @@ msgstr "ПодаваниÑ"
msgid "Commits feed"
msgstr "Поток от подаваниÑ"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "ИÑториÑ"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Подадено от"
msgid "Compare"
msgstr "Сравнение"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -884,6 +1094,9 @@ msgstr "РъководÑтво за ÑътрудничеÑтво"
msgid "Contributors"
msgstr "Сътрудници"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -905,9 +1118,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "Копиране на адреÑа в буфера за обмен"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Копиране на идентификатора на подаването в буфера за обмен"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Създаване на нова папка"
@@ -926,6 +1148,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Създаване на заÑвка за Ñливане"
@@ -938,6 +1163,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Създаване на нов…"
@@ -959,6 +1187,9 @@ msgstr "ЧаÑова зона за „Cron“"
msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Ð½Ð° „Cron“"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "ПерÑонализирани ÑÑŠÐ±Ð¸Ñ‚Ð¸Ñ Ð·Ð° извеÑÑ‚Ñване"
@@ -968,9 +1199,6 @@ msgstr "ПерÑонализираните нива на извеÑÑ‚Ñване
msgid "Cycle Analytics"
msgstr "Ðнализ на циклите"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "Ðнализът на циклите дава общ поглед върху това колко време е нужно на една Ð¸Ð´ÐµÑ Ð´Ð° Ñе превърне в завършена функционалноÑÑ‚ в проекта."
-
msgid "CycleAnalyticsStage|Code"
msgstr "Програмиране"
@@ -1027,12 +1255,21 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Име на папката"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1058,7 +1295,7 @@ msgid "Download zip"
msgstr "СвалÑне във формат „zip“"
msgid "DownloadArtifacts|Download"
-msgstr "СвалÑне"
+msgstr "СвалÑне на"
msgid "DownloadCommit|Email Patches"
msgstr "Изпращане на кръпките по е-поща"
@@ -1069,15 +1306,24 @@ msgstr "Обикновен файл Ñ Ñ€Ð°Ð·Ð»Ð¸ÐºÐ¸"
msgid "DownloadSource|Download"
msgstr "СвалÑне"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Редактиране"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Редактиране на плана %{id} за Ñхема"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1096,9 +1342,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1141,9 +1384,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1171,6 +1438,9 @@ msgstr "Ð’Ñеки меÑец (на 1-во чиÑло, в 4 ч. Ñутринта
msgid "Every week (Sundays at 4:00am)"
msgstr "Ð’ÑÑка Ñедмица (в неделÑ, в 4 ч. Ñутринта)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1189,6 +1459,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1233,10 +1506,10 @@ msgstr "От прилагането на заÑвката за Ñливане д
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1245,16 +1518,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1263,12 +1620,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Към Вашето разклонение"
@@ -1278,6 +1647,9 @@ msgstr "Разклонение"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1314,7 +1686,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1365,6 +1737,11 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr ""
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1418,6 +1801,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1436,6 +1822,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Изключено"
@@ -1445,6 +1852,9 @@ msgstr "Включено"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "ПоÑÐ»ÐµÐ´Ð½Ð¸Ñ %d ден"
@@ -1474,6 +1884,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Ðаучете повече в"
@@ -1492,14 +1905,18 @@ msgstr "ÐапуÑкане на проекта"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Ограничено до показване на най-много %d Ñъбитие"
-msgstr[1] "Ограничено до показване на най-много %d ÑъбитиÑ"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1509,12 +1926,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1527,6 +1953,9 @@ msgstr "Медиана"
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1536,9 +1965,30 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "добавите SSH ключ"
@@ -1548,10 +1998,16 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
@@ -1559,6 +2015,12 @@ msgid_plural "New Issues"
msgstr[0] "Ðов проблем"
msgstr[1] "Ðови проблема"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Ðов план за Ñхема"
@@ -1583,6 +2045,9 @@ msgstr ""
msgid "New issue"
msgstr "Ðов проблем"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Ðова заÑвка за Ñливане"
@@ -1601,7 +2066,22 @@ msgstr ""
msgid "New tag"
msgstr "Ðов етикет"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1616,9 +2096,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "Ðе е налично"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "ÐÑма доÑтатъчно данни"
@@ -1679,6 +2165,12 @@ msgstr "Ðаблюдение"
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1688,7 +2180,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1703,6 +2195,9 @@ msgstr "Филтър"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1736,9 +2231,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr "Схема"
@@ -1781,12 +2273,6 @@ msgstr "Ð’Ñички"
msgid "PipelineSchedules|Inactive"
msgstr "Ðеактивно"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Въведете ключ за променливата"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Въведете ÑтойноÑтта на променливата"
-
msgid "PipelineSchedules|Next Run"
msgstr "Следващо изпълнение"
@@ -1796,9 +2282,6 @@ msgstr "Ðищо"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Въведете кратко опиÑание за тази Ñхема"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Премахване на реда за променлива"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Поемане на ÑобÑтвеноÑтта"
@@ -1826,6 +2309,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "вÑички"
@@ -1838,12 +2327,21 @@ msgstr "Ñ ÐµÑ‚Ð°Ð¿"
msgid "Pipeline|with stages"
msgstr "Ñ ÐµÑ‚Ð°Ð¿Ð¸"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1889,6 +2387,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1904,6 +2405,15 @@ msgstr "Проектът „%{project_name}“ беше обновен уÑпеÑ
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтъпът до проекта Ñ‚Ñ€Ñбва да бъде даван поотделно на вÑеки потребител."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1922,6 +2432,21 @@ msgstr "ИзнаÑÑнето на проекта започна. Ще получ
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Изключено"
@@ -1946,15 +2471,9 @@ msgstr "Графика"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2024,6 +2543,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2039,6 +2561,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Прочетете повече"
@@ -2051,6 +2576,12 @@ msgstr "Клони"
msgid "RefSwitcher|Tags"
msgstr "Етикети"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2075,9 +2606,18 @@ msgstr "Свързани приложени заÑвки за Ñливане"
msgid "Remind later"
msgstr "ÐапомнÑне по-къÑно"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Премахване на проекта"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2093,6 +2633,11 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "ОтмÑна на това подаване"
@@ -2102,15 +2647,15 @@ msgstr "ОтмÑна на тази заÑвка за Ñливане"
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "Запазване на плана за Ñхема"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Създаване на нов план за Ñхема"
@@ -2126,38 +2671,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "ТърÑете в клоните и етикетите"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Изберете формата на архива"
msgid "Select a timezone"
msgstr "Изберете чаÑова зона"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Изберете целеви клон"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Задайте парола на акаунта Ñи, за да можете да изтеглÑте и изпращате промени чрез %{protocol}."
-msgid "Set up CI"
-msgstr "ÐаÑтройка на ÐИ"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "ÐаÑтройка на „Koding“"
@@ -2171,6 +2737,15 @@ msgstr "зададете парола"
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2185,9 +2760,6 @@ msgstr[1] "Показване на %d ÑъбитиÑ"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2200,18 +2772,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2341,10 +2925,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2442,7 +3026,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2457,10 +3044,10 @@ msgstr "Връзката на разклонение беше премахнат
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Етапът на проблемите показва колко е времето от Ñъздаването на проблем до определÑнето на целеви етап на проекта за него, или до добавÑнето му в ÑпиÑък на дъÑката за проблеми. Започнете да добавÑте проблеми, за да видите данните за този етап."
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2469,9 +3056,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Етапът от цикъла на разработка"
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "Планът за Ñхемата ще изпълнÑва Ñхемите в бъдеще, периодично, за определени клони или етикети. Тези планирани Ñхеми ще наÑледÑÑ‚ ограничениÑта на доÑтъпа до проекта на ÑÐ²ÑŠÑ€Ð·Ð°Ð½Ð¸Ñ Ñ Ñ‚ÑÑ… потребител."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Етапът на планиране показва колко е времето от преходната Ñтъпка до изпращането на първото подаване. Това време ще бъде добавено автоматично Ñлед като изпратите първото Ñи подаване."
@@ -2502,19 +3086,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "Времето, което отнема вÑеки Ð·Ð°Ð¿Ð¸Ñ Ð¾Ñ‚ данни за ÑÑŠÐ¾Ñ‚Ð²ÐµÑ‚Ð½Ð¸Ñ ÐµÑ‚Ð°Ð¿."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "СтойноÑтта, коÑто Ñе намира в Ñредата на поÑледователноÑтта от наблюдавани данни. Ðапример: медианата на 3, 5 и 9 е 5, а медианата на 3, 5, 7 и 8 е (5+7)/2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2523,18 +3134,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Това означава, че нÑма да можете да изпращате код, докато не Ñъздадете празно хранилище или не внеÑете ÑъщеÑтвуващо такова."
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2547,9 +3185,21 @@ msgstr "Време преди работата по проблем да запо
msgid "Time between merge request creation and merge/close"
msgstr "Време между Ñъздаване на заÑвка за Ñливане и прилагането/отхвърлÑнето Ñ"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Време преди първата заÑвка за Ñливане"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "преди %s дни"
@@ -2689,6 +3339,18 @@ msgstr "Ñек"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Общо време"
@@ -2704,19 +3366,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "Без звезда"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2740,6 +3423,9 @@ msgstr "Качване на нов файл"
msgid "Upload file"
msgstr "Качване на файл"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "щракнете за качване"
@@ -2752,9 +3438,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr "Използване на глобалната Ви наÑтройка за извеÑтиÑта"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Преглед на отворената заÑвка за Ñливане"
@@ -2776,6 +3468,9 @@ msgstr "ÐеизвеÑтно"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "ИÑкате ли да видите данните? Помолете админиÑтратор за доÑтъп."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "ÐÑма доÑтатъчно данни за този етап."
@@ -2788,9 +3483,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2809,6 +3501,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2911,9 +3609,21 @@ msgstr "Ðа път Ñте да премахнете връзката на раÐ
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Ðа път Ñте да прехвърлите „%{project_name_with_namespace}“ към друг ÑобÑтвеник. ÐÐИСТИÐРли иÑкате това?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Можете да добавÑте файлове Ñамо когато Ñе намирате в клон"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2953,6 +3663,12 @@ msgstr "ÐÑма да можете да изтеглÑте или изпраща
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2965,26 +3681,220 @@ msgstr "Вашето име"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "ден"
msgstr[1] "дни"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "нова заÑвка за Ñливане"
msgid "notification emails"
msgstr "извеÑÑ‚Ð¸Ñ Ð¿Ð¾ е-поща"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "родител"
@@ -2996,12 +3906,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index 5b33ed0a628..4a0ca1e7efb 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:41-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:00-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: German\n"
"Language: de_DE\n"
@@ -16,23 +16,41 @@ msgstr ""
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d Commit"
msgstr[1] "%d Commits"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
msgstr[1] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s zusätzlicher Commit wurde ausgelassen um Leistungsprobleme zu verhindern."
msgstr[1] "%s zusätzliche Commits wurden ausgelassen um Leistungsprobleme zu verhindern."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} hat %{commit_timeago} committet"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -45,9 +63,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} von %{maximum_failures} Fehlschlägen. GitLab wird den Zugriff beim nächsten Versuch zulassen."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} von %{maximum_failures} Fehlschlägen. GitLab wird den Zugriff für %{number_of_seconds} Sekunden blockieren."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} von %{maximum_failures} Fehlschlägen. GitLab wird es nicht weiter versuchen. Setze die Speicherinformation nach Behebung des Problems zurück."
@@ -121,24 +136,81 @@ msgstr "Lizenz hinzufügen"
msgid "Add new directory"
msgstr "Erstelle eine neues Verzeichnis"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr "Alle"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -163,9 +235,6 @@ msgstr "Bist Du sicher, dass Du diesen Pipeline-Zeitplan löschen möchtest?"
msgid "Are you sure you want to discard your changes?"
msgstr "Bist Du sicher, dass Du alle Änderungen zurücksetzen willst?"
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr "Bist Du sicher, dass Du den Registrierungstoken zurücksetzen willst?"
@@ -178,6 +247,21 @@ msgstr "Bist Du sicher?"
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Datei mittels Drag &amp; Drop oder %{upload_link} hinzufügen"
@@ -193,13 +277,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -223,6 +310,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -277,6 +370,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Zweig"
@@ -405,8 +501,8 @@ msgstr "von"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "CI-Konfiguration"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -417,6 +513,9 @@ msgstr "Abbrechen"
msgid "Cancel edit"
msgstr "Bearbeitung abbrechen"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -432,15 +531,24 @@ msgstr "Herauspicken"
msgid "ChangeTypeAction|Revert"
msgstr "Wiederherstellen "
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "Änderungsliste "
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Diagramme"
msgid "Chat"
msgstr "Chat"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -453,7 +561,19 @@ msgstr "Diesen Commit herauspicken "
msgid "Cherry-pick this merge request"
msgstr "Diesen Merge Request herauspicken"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -510,79 +630,91 @@ msgstr "übersprungen"
msgid "CiStatus|running"
msgstr "laufend"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -591,37 +723,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -639,64 +768,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -708,16 +867,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,7 +888,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -747,25 +909,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -777,7 +939,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -795,6 +957,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "Kommentare"
@@ -812,6 +977,9 @@ msgstr "Dauer der Commits in Minuten für die letzten 30 Commits"
msgid "Commit message"
msgstr "Commit Nachricht"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Commit"
@@ -824,15 +992,57 @@ msgstr "Commits"
msgid "Commits feed"
msgstr "Liste der Commits"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Verlauf"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Committed von"
msgid "Compare"
msgstr "Vergleichen"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -884,6 +1094,9 @@ msgstr "Mitarbeitsanleitung"
msgid "Contributors"
msgstr "Mitarbeiter"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -905,9 +1118,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "Kopiere URL in die Zwischenablage"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Kopiere Commit SHA in die Zwischenablage"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Erstelle neues Verzeichnis"
@@ -926,6 +1148,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Erstelle Merge Request"
@@ -938,6 +1163,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Erstelle neues..."
@@ -959,6 +1187,9 @@ msgstr "Cron Zeitzone"
msgid "Cron syntax"
msgstr "Cron Syntax"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Individuelle Benachrichtigungsereignisse"
@@ -968,9 +1199,6 @@ msgstr "Individuelle Benachrichtigungsstufen sind identisch mit den Beteiligungs
msgid "Cycle Analytics"
msgstr "Arbeitsablaufsanalysen"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "Arbeitsablaufsanalysen verschaffen einen Überblick, welche Zeit Dein Projekt von der Idee zur Realisierung benötigt."
-
msgid "CycleAnalyticsStage|Code"
msgstr "Entwicklung"
@@ -1027,12 +1255,21 @@ msgstr ""
msgid "Details"
msgstr "Details"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Verzeichnisname"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "Änderungen verwerfen"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1069,15 +1306,24 @@ msgstr "Unterschiede"
msgid "DownloadSource|Download"
msgstr "Herunterladen"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Bearbeiten"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Pipeline Zeitplan bearbeiten %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "E-Mails"
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1096,9 +1342,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1141,9 +1384,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "Filtere alle"
@@ -1171,6 +1438,9 @@ msgstr "Monatlich (am Ersten um 4:00 Uhr)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Wöchentlich (Sonntags um 4:00 Uhr)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1189,6 +1459,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1233,10 +1506,10 @@ msgstr "Vom Umsetzen des Merge Request bis zur Bereitstellung auf dem Produktivs
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1245,16 +1518,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1263,12 +1620,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Informationen über den Speicherzustand von Gitlab wurden zurückgesetzt."
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "GitLab Runner Bereich"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Gehe zu Deinem Ableger"
@@ -1278,6 +1647,9 @@ msgstr "Ableger"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1314,7 +1686,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1365,6 +1737,11 @@ msgstr "Keine Probleme erkannt"
msgid "HealthCheck|Unhealthy"
msgstr "Problematisch"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr ""
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1418,6 +1801,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1436,6 +1822,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Deaktiviert"
@@ -1445,6 +1852,9 @@ msgstr "Aktiviert"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Letzten %d Tag"
@@ -1474,6 +1884,9 @@ msgstr "Du übertrugst an"
msgid "LastPushEvent|at"
msgstr "am"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Erfahre mehr in den"
@@ -1492,14 +1905,18 @@ msgstr "Verlasse das Projekt"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Limitiere die Anzeige auf höchstens %d Ereignis"
-msgstr[1] "Limitiere die Anzeige auf höchstens %d Ereignisse"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1509,12 +1926,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1527,6 +1953,9 @@ msgstr "Median"
msgid "Members"
msgstr "Mitglieder"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1536,9 +1965,30 @@ msgstr "Ereignisse zusammenführen"
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "Nachrichten"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "einen SSH Schlüssel hinzufügst"
@@ -1548,10 +1998,16 @@ msgstr "Ãœberwachung"
msgid "More information is available|here"
msgstr "hier"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
@@ -1559,6 +2015,12 @@ msgid_plural "New Issues"
msgstr[0] "Neues Ticket"
msgstr[1] "Neue Tickets"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Neuer Pipeline Zeitplan"
@@ -1583,6 +2045,9 @@ msgstr ""
msgid "New issue"
msgstr "Neues Ticket"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Neuer Merge Request"
@@ -1601,7 +2066,22 @@ msgstr ""
msgid "New tag"
msgstr "Neuer Tag"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1616,9 +2096,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "Nicht verfügbar"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "Nicht genügend Daten"
@@ -1679,6 +2165,12 @@ msgstr "Beobachten"
msgid "Notifications"
msgstr "Benachrichtigungen"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1688,7 +2180,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1703,6 +2195,9 @@ msgstr "Filter"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1736,9 +2231,6 @@ msgstr ""
msgid "Password"
msgstr "Passwort"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr ""
@@ -1781,12 +2273,6 @@ msgstr "Alle"
msgid "PipelineSchedules|Inactive"
msgstr "Inaktiv"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Schlüssel der Eingangsvariable"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Wert der Eingangsvariable"
-
msgid "PipelineSchedules|Next Run"
msgstr "Nächste Durchführung"
@@ -1796,9 +2282,6 @@ msgstr "Nichts"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Beschreibe diese Pipeline"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Entferne Variablenreihe"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Eigentümer werden"
@@ -1826,6 +2309,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr "Pipelines des letzten Jahres"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "Alle"
@@ -1838,12 +2327,21 @@ msgstr "mit Stage"
msgid "Pipeline|with stages"
msgstr "mit Stages"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1889,6 +2387,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1904,6 +2405,15 @@ msgstr "Das Projekt '%{project_name}' wurde erfolgreich aktualisiert."
msgid "Project access must be granted explicitly to each user."
msgstr "Jedem Nutzer muss explizit der Zugriff auf das Projekt gewährt werden."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "Projektdetails"
@@ -1922,6 +2432,21 @@ msgstr "Export des Projektes gestartet. Ein Link zum herunterladen wir Dir per E
msgid "ProjectActivityRSS|Subscribe"
msgstr "Abonnieren"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Dekativiert"
@@ -1946,15 +2471,9 @@ msgstr "Diagramm"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -1977,7 +2496,7 @@ msgid "ProjectsDropdown|Loading projects"
msgstr ""
msgid "ProjectsDropdown|Projects you visit often will appear here"
-msgstr "ProjectsDropdown | Projekte, die Sie häufig besuchen, werden hier angezeigt"
+msgstr "Projekte, die du häufig besuchst, werden hier angezeigt"
msgid "ProjectsDropdown|Search your projects"
msgstr ""
@@ -2024,6 +2543,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2039,6 +2561,9 @@ msgstr "Ãœbertragungsereignisse"
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Mehr lesen"
@@ -2051,6 +2576,12 @@ msgstr "Branches"
msgid "RefSwitcher|Tags"
msgstr "Tags"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2075,9 +2606,18 @@ msgstr "Zugehörige umgesetzte Merge Requests"
msgid "Remind later"
msgstr "Später erinnern"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Projekt entfernen"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2093,6 +2633,11 @@ msgstr "Zugriffstoken für Systemzustand zurücksetzen"
msgid "Reset runners registration token"
msgstr "Registrierungstoken für Runner zurücksetzen"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "Commit zurücksetzen"
@@ -2102,15 +2647,15 @@ msgstr "Merge Request zurücksetzen"
msgid "SSH Keys"
msgstr "SSH-Schlüssel"
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "Zeitplan der Pipeline speichern"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Plane eine neue Pipeline"
@@ -2126,38 +2671,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "Suche nach Branches und Tags"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Archivierungsformat auswählen"
msgid "Select a timezone"
msgstr "Zeitzone auswählen"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Zielbranch auswählen"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Lege ein Passwort für dein Konto fest, um mittels %{protocol} zu übertragen (push) oder abzurufen (pull)."
-msgid "Set up CI"
-msgstr "CI einrichten"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Koding einrichten"
@@ -2171,6 +2737,15 @@ msgstr "ein Passwort festlegst"
msgid "Settings"
msgstr "Einstellungen"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2185,9 +2760,6 @@ msgstr[1] "Zeige %d Ereignisse"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2200,18 +2772,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2341,10 +2925,10 @@ msgstr "Starte den Runner!"
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2442,7 +3026,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2457,10 +3044,10 @@ msgstr "Die Beziehung des Ablegers wurde entfernt."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Die Ticketphase stellt die Zeit vom Anlegen eines Tickets bis zum Zuweisen eines Meilensteins oder Hinzufügen zur Aufgabentafel dar. Erstelle einen Ticket, damit dessen Daten hier erscheinen."
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2469,9 +3056,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Die Phase des Entwicklungslebenszyklus."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "Die Pipelinezeitpläne starten Pipelines in der Zukunft, wiederholend, für bestimmte Branches oder Tags. Diese geplanten Pipelines haben denselben begrenzten Zugriff auf das Projekt, wie der zugeordnete Nutzer."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Die Planungsphase stellt die Zeit von der vorherigen Phase bis zum Übertragen des ersten Commits dar. Sobald Du den ersten Commit überträgst, werden dessen Daten hier erscheinen."
@@ -2502,19 +3086,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "Zeit, die für das jeweilige Ereignis in der Phase ermittelt wurde."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Der mittlere aller erfassten Werte. Zum Beispiel ist für 3, 5, 9 der Median 5. Bei 3, 5, 7, 8 ist der Median (5+7)/2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Es gibt ein Problem beim Zugriff auf den Gitspeicher:"
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2523,18 +3134,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Dies bedeutet, dass Du keinen Code übertragen kannst, bevor Du kein leeres Repositorium erstellt oder ein Existierendes importiert hast."
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2547,9 +3185,21 @@ msgstr "Zeit bis die Implementierung für ein Ticket beginnt"
msgid "Time between merge request creation and merge/close"
msgstr "Zeit zwischen einem Merge Request und dessen Umsetzung / Schließung"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Zeit bis zum ersten Merge Request"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "vor %s Tagen"
@@ -2689,6 +3339,18 @@ msgstr "Sek."
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Gesamtzeit"
@@ -2704,19 +3366,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "Entfavorisieren"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2740,6 +3423,9 @@ msgstr "Eine Neue Datei hochladen"
msgid "Upload file"
msgstr "Eine Datei hochladen"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "Zum Upload klicken"
@@ -2752,9 +3438,15 @@ msgstr "Benutze den folgenden Registrierungstoken während des Setups:"
msgid "Use your global notification setting"
msgstr "Benutze Deine globalen Benachrichtigungseinstellungen"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Zeige offene Merge Requests."
@@ -2776,6 +3468,9 @@ msgstr "Unbekannt"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Du möchtest diese Daten sehen? Bitte frage einen Administrator nach dem Zugang."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "Es liegen nicht genügend Daten vor, um diese Phase anzuzeigen."
@@ -2788,9 +3483,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr "Wiki"
@@ -2809,6 +3501,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2911,9 +3609,21 @@ msgstr "Du bist dabei, die Beziehung des Ablegers zum Ursprungsprojekt %{forked_
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Du bist dabei %{project_name_with_namespace} einem andere Besitzer zu übergeben. Bist Du dir WIRKLICH sicher?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Du kannst Dateien nur hinzufügen, wenn Du dich auf einem Branch befindest."
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2953,6 +3663,12 @@ msgstr "Du kannst erst mittels SSH übertragen (push) oder abrufen (pull), nachd
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2965,26 +3681,220 @@ msgstr "Dein Name"
msgid "Your projects"
msgstr "Deine Projekte"
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "Tag"
msgstr[1] "Tage"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "Neuer Merge Request"
msgid "notification emails"
msgstr "Benachrichtungsemail"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "Vorgänger"
@@ -2996,12 +3906,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index 54906417e75..7d4648c55f1 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:59-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Esperanto\n"
"Language: eo_UY\n"
@@ -16,23 +16,41 @@ msgstr ""
"X-Crowdin-Language: eo\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d enmetado"
msgstr[1] "%d enmetadoj"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
msgstr[1] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s enmetado estis transsaltita, por ne troÅarÄi la sistemon."
msgstr[1] "%s enmetadoj estis transsaltitaj, por ne troÅarÄi la sistemon."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} enmetis %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -45,9 +63,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
@@ -121,24 +136,81 @@ msgstr "Aldoni rajtigilon"
msgid "Add new directory"
msgstr "Aldoni novan dosierujon"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr ""
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -163,9 +235,6 @@ msgstr "Ĉu vi certe volas forigi ĉi tiun ĉenstablan planon?"
msgid "Are you sure you want to discard your changes?"
msgstr ""
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -178,6 +247,21 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Alkroĉu dosieron per Åovmetado aÅ­ %{upload_link}"
@@ -193,13 +277,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -223,6 +310,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -277,6 +370,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Branĉo"
@@ -405,8 +501,8 @@ msgstr "de"
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
-msgstr "Agordoj de seninterrompa integrado"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -417,6 +513,9 @@ msgstr "Nuligi"
msgid "Cancel edit"
msgstr ""
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -432,15 +531,24 @@ msgstr "Precize elekti"
msgid "ChangeTypeAction|Revert"
msgstr "Malfari"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "Listo de ÅanÄoj"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Diagramoj"
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -453,7 +561,19 @@ msgstr "Precize elekti ĉi tiun kunmetadon"
msgid "Cherry-pick this merge request"
msgstr "Precize elekti ĉi tiun peton pri kunfando"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -510,79 +630,91 @@ msgstr "transsaltita"
msgid "CiStatus|running"
msgstr "plenumiÄanta"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -591,37 +723,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -639,64 +768,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -708,16 +867,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,7 +888,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -747,25 +909,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -777,7 +939,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -795,6 +957,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -812,6 +977,9 @@ msgstr "DaÅ­ro de la enmetadoj por la lastaj 30 enmetadoj"
msgid "Commit message"
msgstr "MesaÄo pri la enmetado"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Enmeti"
@@ -824,15 +992,57 @@ msgstr "Enmetadoj"
msgid "Commits feed"
msgstr "Fluo de enmetadoj"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Historio"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Enmetita de"
msgid "Compare"
msgstr "Kompari"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -884,6 +1094,9 @@ msgstr "Gvidlinioj por kontribuado"
msgid "Contributors"
msgstr "Kontribuantoj"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -905,9 +1118,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "Kopii la adreson en la kopibufron"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Kopii la identigilon de la enmetado"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Krei novan dosierujon"
@@ -926,6 +1148,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Krei peton pri kunfando"
@@ -938,6 +1163,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Krei novan…"
@@ -959,6 +1187,9 @@ msgstr "Horzono por Cron"
msgid "Cron syntax"
msgstr "La sintakso de Cron"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Propraj sciigaj eventoj"
@@ -968,9 +1199,6 @@ msgstr "La propraj sciigaj niveloj estas la samaj kiel la niveloj de partoprenad
msgid "Cycle Analytics"
msgstr "Cikla analizo"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "La cikla analizo esploras kiom da tempo necesas por disvolvi ideon Äis Äi fariÄos realaĵo."
-
msgid "CycleAnalyticsStage|Code"
msgstr "Programado"
@@ -1027,12 +1255,21 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Nomo de dosierujo"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1069,15 +1306,24 @@ msgstr "Normala dosiero kun diferencoj"
msgid "DownloadSource|Download"
msgstr "ElÅuti"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Redakti"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Redakti ĉenstablan planon %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1096,9 +1342,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1141,9 +1384,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1171,6 +1438,9 @@ msgstr "Ĉiumonate (en la 1a de la monato, je 4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Ĉiusemajne (en dimanĉo, je 4:00)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1189,6 +1459,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1233,10 +1506,10 @@ msgstr "De la kunfandado de la peto pri kunfando Äis la disponigado en la publi
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1245,16 +1518,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1263,12 +1620,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Al via disbranĉigo"
@@ -1278,6 +1647,9 @@ msgstr "Disbranĉigo"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1314,7 +1686,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1365,6 +1737,11 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr ""
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1418,6 +1801,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1436,6 +1822,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "MalÅaltita"
@@ -1445,6 +1852,9 @@ msgstr "Åœaltita"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "La lasta %d tago"
@@ -1474,6 +1884,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Lernu pli en la"
@@ -1492,14 +1905,18 @@ msgstr "Forlasi la projekton"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Limigita al montrado de ne pli ol %d evento"
-msgstr[1] "Limigita al montrado de ne pli ol %d eventoj"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1509,12 +1926,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1527,6 +1953,9 @@ msgstr "Mediano"
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1536,9 +1965,30 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "aldonos SSH-Ålosilon"
@@ -1548,10 +1998,16 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
@@ -1559,6 +2015,12 @@ msgid_plural "New Issues"
msgstr[0] "Nova problemo"
msgstr[1] "Novaj problemoj"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nova ĉenstabla plano"
@@ -1583,6 +2045,9 @@ msgstr ""
msgid "New issue"
msgstr "Nova problemo"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Nova peto pri kunfando"
@@ -1601,7 +2066,22 @@ msgstr ""
msgid "New tag"
msgstr "Nova etikedo"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1616,9 +2096,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "Ne disponebla"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "Ne estas sufiĉe da datenoj"
@@ -1679,6 +2165,12 @@ msgstr "Rigardado"
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1688,7 +2180,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1703,6 +2195,9 @@ msgstr "Filtrilo"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1736,9 +2231,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr "Ĉenstablo"
@@ -1781,12 +2273,6 @@ msgstr "Ĉiuj"
msgid "PipelineSchedules|Inactive"
msgstr "MalÅaltitaj"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Entajpu Ålosilon por la variablo"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Entajpu la valoron de la variablo"
-
msgid "PipelineSchedules|Next Run"
msgstr "Sekvanta plenumo"
@@ -1796,9 +2282,6 @@ msgstr "Nenio"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Entajpu mallongan priskribon pri ĉi tiu ĉenstablo"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Forigi la variablan linion"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Akiri posedon"
@@ -1826,6 +2309,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "ĉiuj"
@@ -1838,12 +2327,21 @@ msgstr "kun etapo"
msgid "Pipeline|with stages"
msgstr "kun etapoj"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1889,6 +2387,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1904,6 +2405,15 @@ msgstr "La projekto „%{project_name}“ estis sukcese Äisdatigita."
msgid "Project access must be granted explicitly to each user."
msgstr "Ĉiu uzanto devas akiri propran atingon al la projekto."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1922,6 +2432,21 @@ msgstr "La elporto de la projekto komenciÄis. Vi ricevos ligilon per retpoÅto
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "MalÅaltita"
@@ -1946,15 +2471,9 @@ msgstr "Grafeo"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2024,6 +2543,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2039,6 +2561,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Legu pli"
@@ -2051,6 +2576,12 @@ msgstr "Branĉoj"
msgid "RefSwitcher|Tags"
msgstr "Etikedoj"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2075,9 +2606,18 @@ msgstr "Rilataj aplikitaj petoj pri kunfando"
msgid "Remind later"
msgstr "Rememorigu denove"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Forigi la projekton"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2093,6 +2633,11 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "Malfari ĉi tiun enmetadon"
@@ -2102,15 +2647,15 @@ msgstr "Malfari ĉi tiun peton pri kunfando"
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "Konservi ĉenstablan planon"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Plani novan ĉenstablon"
@@ -2126,38 +2671,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "Serĉu branĉon aŭ etikedon"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Elektu formaton de arkivo"
msgid "Select a timezone"
msgstr "Elektu horzonon"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Elektu celan branĉon"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Kreu pasvorton por via konto por ebligi al vi eltiri kaj alpuÅi per %{protocol}."
-msgid "Set up CI"
-msgstr "Agordi SI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Agordi „Koding“"
@@ -2171,6 +2737,15 @@ msgstr "kreos pasvorton"
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2185,9 +2760,6 @@ msgstr[1] "Estas montrataj %d eventoj"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2200,18 +2772,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2341,10 +2925,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2442,7 +3026,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2457,10 +3044,10 @@ msgstr "La rilato de disbranĉigo estis forigita."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "La etapo de la problemo montras kiom la tempo pasas de la kreado de problemo Äis la atribuado de la problemo al cela etapo de la projekto, aÅ­ al listo sur la problemtabulo. Komencu krei problemojn por vidi la datenojn por ĉi tiu etapo."
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2469,9 +3056,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "La etapo de la disvolva ciklo."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "La ĉenstabla plano plenumas ĉenstablojn en la estonteco, ripete, por difinitaj branĉoj aŭ etikedoj. Tiuj planitaj ĉenstabloj heredos la limigitan atingon al la projekto de la rilata uzanto."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "La etapo de la plano montras la tempon de la antaÅ­a Åtupo Äis la alpuÅado de via unua enmetado. Ĉi tiu tempo aldoniÄos aÅ­tomate post kiam vi alpuÅas la unuan enmetadon."
@@ -2502,19 +3086,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "La tempo, kiu estas necesa por ĉiu dateno kolektita de la etapo."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "La valoro, kiu troviÄas en la mezo de aro da rigardataj valoroj. Ekzemple: inter 3, 5 kaj 9, la mediano estas 5. Inter 3, 5, 7 kaj 8, la mediano estas (5+7)/2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2523,18 +3134,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Ĉi tiu signifas, ke vi ne povos alpuÅi kodon, antaÅ­ ol vi kreos malplenan deponejon aÅ­ enportos jam ekzistantan."
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2547,9 +3185,21 @@ msgstr "Tempo antaÅ­ la komenco de laboro super problemo"
msgid "Time between merge request creation and merge/close"
msgstr "Tempo inter la kreado de poeto pri kunfando kaj Äia aplikado/fermado"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Tempo Äis la unua peto pri kunfando"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "antaÅ­ %s tagoj"
@@ -2689,6 +3339,18 @@ msgstr "s"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Totala tempo"
@@ -2704,19 +3366,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "Malsteligi"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2740,6 +3423,9 @@ msgstr "AlÅuti novan dosieron"
msgid "Upload file"
msgstr "AlÅuti dosieron"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "alklaku por alÅuti"
@@ -2752,9 +3438,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr "Uzi vian Äeneralan agordon pri la sciigoj"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Vidi la malfermitan peton pri kunfando"
@@ -2776,6 +3468,9 @@ msgstr "Nekonata"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Ĉu vi volas vidi la datenojn? Bonvolu peti atingeblon de administranto."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "Ne estas sufiĉe da datenoj por montri ĉi tiun etapon."
@@ -2788,9 +3483,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2809,6 +3501,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2911,9 +3609,21 @@ msgstr "Vi forigos la rilaton de la disbranĉigo al la originala projekto, „%{
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Vi estas transigonta „%{project_name_with_namespace}“ al alia posedanto. Ĉu vi estas ABSOLUTE certa?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Oni povas aldoni dosierojn nur kiam oni estas en branĉo"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2953,6 +3663,12 @@ msgstr "Vi ne povos eltiri aÅ­ alpuÅi kodon per SSH antaÅ­ ol vi %{add_ssh_key_
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2965,26 +3681,220 @@ msgstr "Via nomo"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "tago"
msgstr[1] "tagoj"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "novan peton pri kunfando"
msgid "notification emails"
msgstr "sciigoj per retpoÅto"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "patro"
@@ -2996,12 +3906,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index 750dde61a03..7d28a7064d3 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:41-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:01-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Spanish\n"
"Language: es_ES\n"
@@ -16,13 +16,31 @@ msgstr ""
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d cambio"
msgstr[1] "%d cambios"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
+msgstr[0] "%d capa"
+msgstr[1] "%d capas"
+
+msgid "%d merge request"
+msgid_plural "%d merge requests"
msgstr[0] ""
msgstr[1] ""
@@ -31,50 +49,47 @@ msgid_plural "%s additional commits have been omitted to prevent performance iss
msgstr[0] "%s cambio adicional ha sido omitido para evitar problemas de rendimiento."
msgstr[1] "%s cambios adicionales han sido omitidos para evitar problemas de rendimiento."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} cambió %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{count} participante"
+msgstr[1] "%{count} participantes"
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
-msgstr ""
+msgstr "%{number_commits_behind} commits detrás de %{default_branch}, %{number_commits_ahead} commits por delante"
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
-msgstr ""
-
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
+msgstr "%{number_of_failures} de %{maximum_failures} intentos fallidos. GitLab permitirá el acceso en el siguiente intento."
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
-msgstr ""
+msgstr "%{number_of_failures} de %{maximum_failures} intentos fallidos. GitLab no reintentará automáticamente. Debe reinicializar la información de almacenamiento cuando el problema sea solventado."
msgid "%{storage_name}: failed storage access attempt on host:"
msgid_plural "%{storage_name}: %{failed_attempts} failed storage access attempts:"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{storage_name}: intento de acceso fallido al almacenamiento en host:"
+msgstr[1] "%{storage_name}: %{failed_attempts} intentos de acceso fallido al almacenamiento:"
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} esta disponible"
msgid "(checkout the %{link} for information on how to install it)."
-msgstr ""
+msgstr "(para obtener información sobre cómo instalarlo visite %{link})."
msgid "+ %{moreCount} more"
-msgstr ""
+msgstr "+ %{moreCount} más"
msgid "- show less"
-msgstr ""
+msgstr "- mostrar menos"
msgid "1 pipeline"
msgid_plural "%d pipelines"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 pipeline"
+msgstr[1] "%d pipelines"
msgid "1st contribution!"
-msgstr ""
+msgstr "¡1ra contribución!"
msgid "2FA enabled"
msgstr ""
@@ -86,16 +101,16 @@ msgid "About auto deploy"
msgstr "Acerca del auto despliegue"
msgid "Abuse Reports"
-msgstr ""
+msgstr "Reportes de abuso"
msgid "Access Tokens"
-msgstr ""
+msgstr "Tokens de acceso"
msgid "Access to failing storages has been temporarily disabled to allow the mount to recover. Reset storage information after the issue has been resolved to allow access again."
msgstr ""
msgid "Account"
-msgstr ""
+msgstr "Cuenta"
msgid "Active"
msgstr "Activo"
@@ -104,7 +119,7 @@ msgid "Activity"
msgstr "Actividad"
msgid "Add"
-msgstr ""
+msgstr "Añadir"
msgid "Add Changelog"
msgstr "Agregar Changelog"
@@ -113,7 +128,7 @@ msgid "Add Contribution guide"
msgstr "Agregar guía de contribución"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Añadir Webhooks Grupales y Gitlab Enterprise Edition."
msgid "Add License"
msgstr "Agregar Licencia"
@@ -121,38 +136,95 @@ msgstr "Agregar Licencia"
msgid "Add new directory"
msgstr "Agregar nuevo directorio"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
+msgstr "Página de estado"
+
+msgid "Advanced"
msgstr ""
msgid "Advanced settings"
-msgstr ""
+msgstr "Configuración avanzada"
msgid "All"
msgstr ""
-msgid "An error occurred when toggling the notification subscription"
+msgid "All changes are committed"
msgstr ""
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
+msgid "An error occurred when toggling the notification subscription"
+msgstr "Se produjo un error al activar/desactivar la suscripción de notificación"
+
msgid "An error occurred when updating the issue weight"
+msgstr "Se produjo un error al actualizar el peso de la incidencia"
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
msgstr ""
msgid "An error occurred while fetching sidebar data"
+msgstr "Se produjo un error al obtener datos de la barra lateral"
+
+msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred. Please try again."
+msgid "An error occurred while loading filenames"
msgstr ""
-msgid "Appearance"
+msgid "An error occurred while rendering KaTeX"
msgstr ""
-msgid "Applications"
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
msgstr ""
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
+msgid "An error occurred. Please try again."
+msgstr "Se produjo un error. Por favor inténtelo de nuevo."
+
+msgid "Appearance"
+msgstr "Apariencia"
+
+msgid "Applications"
+msgstr "Aplicaciones"
+
msgid "Apr"
msgstr ""
msgid "April"
-msgstr ""
+msgstr "Abril"
msgid "Archived project! Repository is read-only"
msgstr "¡Proyecto archivado! El repositorio es de solo lectura"
@@ -161,45 +233,60 @@ msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
msgid "Are you sure you want to discard your changes?"
-msgstr ""
+msgstr "¿Está seguro que desea descartar sus cambios?"
+
+msgid "Are you sure you want to reset registration token?"
+msgstr "¿Está seguro que desea reinicializar el token de registro?"
+
+msgid "Are you sure you want to reset the health check token?"
+msgstr "¿Está seguro que desea reinicializar el token de Verificación de Estado?"
+
+msgid "Are you sure?"
+msgstr "¿Estás seguro?"
+
+msgid "Artifacts"
+msgstr "Artefactos"
-msgid "Are you sure you want to leave this group?"
+msgid "Assign custom color like #FF0000"
msgstr ""
-msgid "Are you sure you want to reset registration token?"
+msgid "Assign labels"
msgstr ""
-msgid "Are you sure you want to reset the health check token?"
+msgid "Assign milestone"
msgstr ""
-msgid "Are you sure?"
+msgid "Assign to"
msgstr ""
-msgid "Artifacts"
+msgid "Assignee"
msgstr ""
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Adjunte un archivo arrastrando &amp; soltando o %{upload_link}"
msgid "Aug"
-msgstr ""
+msgstr "Ago"
msgid "August"
-msgstr ""
+msgstr "Agosto"
msgid "Authentication Log"
-msgstr ""
+msgstr "Registro de Autenticación"
msgid "Author"
+msgstr "Autor"
+
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -221,11 +308,17 @@ msgid "AutoDevOps|You can activate %{link_to_settings} for this project."
msgstr ""
msgid "Available"
+msgstr "Disponible"
+
+msgid "Avatar will be removed. Are you sure?"
msgstr ""
-msgid "Billing"
+msgid "Average per day: %{average}"
msgstr ""
+msgid "Billing"
+msgstr "Facturación"
+
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
msgstr ""
@@ -277,6 +370,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Rama"
@@ -405,8 +501,8 @@ msgstr "por"
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
-msgstr "Configuración de CI"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -417,6 +513,9 @@ msgstr "Cancelar"
msgid "Cancel edit"
msgstr ""
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -432,15 +531,24 @@ msgstr "Cherry-pick"
msgid "ChangeTypeAction|Revert"
msgstr "Revertir"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr ""
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Gráficos"
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -453,7 +561,19 @@ msgstr "Escoger este cambio"
msgid "Cherry-pick this merge request"
msgstr "Escoger esta solicitud de fusión"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -510,79 +630,91 @@ msgstr "omitido"
msgid "CiStatus|running"
msgstr "en ejecución"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|Applications"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -591,37 +723,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -639,64 +768,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -708,16 +867,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,7 +888,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -747,25 +909,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -777,7 +939,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -795,6 +957,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -812,6 +977,9 @@ msgstr "Duración de los cambios en minutos para los últimos 30"
msgid "Commit message"
msgstr "Mensaje del cambio"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Cambio"
@@ -824,15 +992,57 @@ msgstr "Cambios"
msgid "Commits feed"
msgstr "Feed de cambios"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Historial"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Enviado por"
msgid "Compare"
msgstr "Comparar"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -884,6 +1094,9 @@ msgstr "Guía de contribución"
msgid "Contributors"
msgstr "Contribuidores"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -905,9 +1118,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "Copiar URL al portapapeles"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Copiar SHA del cambio al portapapeles"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Crear Nuevo Directorio"
@@ -926,6 +1148,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Crear solicitud de fusión"
@@ -938,6 +1163,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Crear nuevo..."
@@ -959,6 +1187,9 @@ msgstr "Zona horaria del Cron"
msgid "Cron syntax"
msgstr "Sintaxis de Cron"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Eventos de notificaciones personalizadas"
@@ -968,9 +1199,6 @@ msgstr "Los niveles de notificación personalizados son los mismos que los nivel
msgid "Cycle Analytics"
msgstr ""
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "Cycle Analytics ofrece una visión general de cuánto tiempo tarda en pasar de idea a producción en su proyecto."
-
msgid "CycleAnalyticsStage|Code"
msgstr "Código"
@@ -1027,12 +1255,21 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Nombre del directorio"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1069,15 +1306,24 @@ msgstr "Diferencias en texto plano"
msgid "DownloadSource|Download"
msgstr "Descargar"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Editar"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Editar Programación del Pipeline %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1096,9 +1342,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1141,9 +1384,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1171,6 +1438,9 @@ msgstr "Todos los meses (el día 1 a las 4:00 am)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Todas las semanas (domingos a las 4:00 am)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1189,6 +1459,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1233,10 +1506,10 @@ msgstr "Desde la integración de la solicitud de fusión hasta el despliegue a p
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1245,16 +1518,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1263,12 +1620,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Ir a tu bifurcación"
@@ -1278,6 +1647,9 @@ msgstr "Bifurcación"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1314,7 +1686,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1365,6 +1737,11 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr ""
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1418,6 +1801,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1436,6 +1822,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Deshabilitado"
@@ -1445,6 +1852,9 @@ msgstr "Habilitado"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Último %d día"
@@ -1474,6 +1884,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Más información en la"
@@ -1492,14 +1905,18 @@ msgstr "Abandonar proyecto"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Limitado a mostrar máximo %d evento"
-msgstr[1] "Limitado a mostrar máximo %d eventos"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1509,12 +1926,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1527,6 +1953,9 @@ msgstr "Mediana"
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1536,9 +1965,30 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "agregar una clave SSH"
@@ -1548,10 +1998,16 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
@@ -1559,6 +2015,12 @@ msgid_plural "New Issues"
msgstr[0] "Nueva incidencia"
msgstr[1] "Nuevas incidencias"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nueva Programación del Pipeline"
@@ -1583,6 +2045,9 @@ msgstr ""
msgid "New issue"
msgstr "Nueva incidencia"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Nueva solicitud de fusión"
@@ -1601,7 +2066,22 @@ msgstr ""
msgid "New tag"
msgstr "Nueva etiqueta"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1616,9 +2096,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "No disponible"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "No hay suficientes datos"
@@ -1679,6 +2165,12 @@ msgstr "Vigilancia"
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1688,7 +2180,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1703,6 +2195,9 @@ msgstr "Filtrar"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1736,9 +2231,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr ""
@@ -1781,12 +2273,6 @@ msgstr "Todos"
msgid "PipelineSchedules|Inactive"
msgstr "Inactivos"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Ingrese nombre de clave"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Ingrese el valor de la variable"
-
msgid "PipelineSchedules|Next Run"
msgstr "Próxima Ejecución"
@@ -1796,9 +2282,6 @@ msgstr "Ninguno"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Proporcione una descripción breve para este pipeline"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Eliminar fila de variable"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Tomar posesión"
@@ -1826,6 +2309,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "todos"
@@ -1838,12 +2327,21 @@ msgstr "con etapa"
msgid "Pipeline|with stages"
msgstr "con etapas"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1889,6 +2387,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1904,6 +2405,15 @@ msgstr "Proyecto ‘%{project_name}’ fue actualizado satisfactoriamente."
msgid "Project access must be granted explicitly to each user."
msgstr "El acceso al proyecto debe concederse explícitamente a cada usuario."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1922,6 +2432,21 @@ msgstr "Se inició la exportación del proyecto. Se enviará un enlace de descar
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Deshabilitada"
@@ -1946,15 +2471,9 @@ msgstr "Historial gráfico"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2024,6 +2543,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2039,6 +2561,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Leer más"
@@ -2051,6 +2576,12 @@ msgstr "Ramas"
msgid "RefSwitcher|Tags"
msgstr "Etiquetas"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2075,9 +2606,18 @@ msgstr "Solicitudes de fusión Relacionadas"
msgid "Remind later"
msgstr "Recordar después"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Eliminar proyecto"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2093,6 +2633,11 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "Revertir este cambio"
@@ -2102,15 +2647,15 @@ msgstr "Revertir esta solicitud de fusión"
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "Guardar programación del pipeline"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Programar un nuevo pipeline"
@@ -2126,38 +2671,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "Buscar ramas y etiquetas"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Seleccionar formato de archivo"
msgid "Select a timezone"
msgstr "Selecciona una zona horaria"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Selecciona una rama de destino"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}."
-msgid "Set up CI"
-msgstr "Configurar CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Configurar Koding"
@@ -2171,6 +2737,15 @@ msgstr "establecer una contraseña"
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2185,9 +2760,6 @@ msgstr[1] "Mostrando %d eventos"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2200,18 +2772,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2341,10 +2925,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2442,7 +3026,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2457,10 +3044,10 @@ msgstr "La relación con la bifurcación se ha eliminado."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "La etapa de incidencia muestra el tiempo que toma desde la creación de un tema hasta asignar el tema a un hito, o añadir el tema a una lista en el panel de temas. Empieza a crear temas para ver los datos de esta etapa."
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2469,9 +3056,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "La etapa del ciclo de vida de desarrollo."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "La programación de pipelines ejecuta pipelines en el futuro, repetidamente, para ramas o etiquetas específicas. Los pipelines programados heredarán acceso limitado al proyecto basado en su usuario asociado."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "La etapa de planificación muestra el tiempo desde el paso anterior hasta el envío de tu primera confirmación. Este tiempo se añadirá automáticamente una vez que usted envíe el primer cambio."
@@ -2502,19 +3086,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "El tiempo utilizado por cada entrada de datos obtenido por esa etapa."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "El valor en el punto medio de una serie de valores observados. Por ejemplo, entre 3, 5, 9, la mediana es 5. Entre 3, 5, 7, 8, la mediana es (5 + 7) / 2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2523,18 +3134,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Esto significa que no puede enviar código hasta que cree un repositorio vacío o importe uno existente."
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2547,9 +3185,21 @@ msgstr "Tiempo antes de que empieze la implementación de una incidencia"
msgid "Time between merge request creation and merge/close"
msgstr "Tiempo entre la creación de la solicitud de fusión y la integración o cierre de ésta"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Tiempo hasta la primera solicitud de fusión"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "hace %s días"
@@ -2689,6 +3339,18 @@ msgstr "s"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Tiempo Total"
@@ -2704,19 +3366,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "No Destacar"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2740,6 +3423,9 @@ msgstr "Subir nuevo archivo"
msgid "Upload file"
msgstr "Subir archivo"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "Hacer clic para subir"
@@ -2752,9 +3438,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr "Utiliza tu configuración de notificación global"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Ver solicitud de fusión abierta"
@@ -2776,6 +3468,9 @@ msgstr "Desconocido"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "¿Quieres ver los datos? Por favor pide acceso al administrador."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "No hay suficientes datos para mostrar en esta etapa."
@@ -2788,9 +3483,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2809,6 +3501,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2911,9 +3609,21 @@ msgstr "Vas a eliminar el enlace de la bifurcación con el proyecto original %{f
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Vas a transferir %{project_name_with_namespace} a otro propietario. ¿Estás TOTALMENTE seguro?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Solo puedes agregar archivos cuando estás en una rama"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2953,6 +3663,12 @@ msgstr "No podrás actualizar o enviar código al proyecto a través de SSH hast
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2965,26 +3681,220 @@ msgstr "Tu nombre"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "día"
msgstr[1] "días"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "nueva solicitud de fusión"
msgid "notification emails"
msgstr "correos electrónicos de notificación"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "padre"
@@ -2996,12 +3906,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 2a713917684..d176e67937f 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:40-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:59-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: French\n"
"Language: fr_FR\n"
@@ -16,23 +16,41 @@ msgstr ""
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
-msgstr[0] "%d validation"
-msgstr[1] "%d validations"
+msgstr[0] "%d commit"
+msgstr[1] "%d commits"
+
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d couche"
msgstr[1] "%d couches"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s validation supplémentaire a été masquée afin d'éviter de créer de problèmes de performances."
msgstr[1] "%s validations supplémentaires ont été masquées afin d'éviter de créer de problèmes de performances."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} a validé %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -45,9 +63,6 @@ msgstr "%{number_commits_behind} validations de retard sur %{default_branch}, %{
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} sur %{maximum_failures} tentative(s). GitLab va vous permettre d'accéder à la prochaine tentative."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab va bloquer l’accès pendant %{number_of_seconds} secondes."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} échecs sur %{maximum_failures}. GitLab ne va plus réessayer automatiquement. Réinitialisez les informations de stockage lorsque le problème est résolu."
@@ -121,24 +136,81 @@ msgstr "Ajouter une licence"
msgid "Add new directory"
msgstr "Ajouter un nouveau dossier"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "État des services"
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr "Paramètres avancés"
msgid "All"
msgstr "Tous"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr "Une erreur s’est produite lors de l’activation/désactivation de l’abonnement aux notifications"
msgid "An error occurred when updating the issue weight"
msgstr "Une erreur s'est produite lors de la mise à jour du poids du ticket"
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr "Une erreur s'est produite lors de la récupération des données de la barre latérale"
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr "Une erreur est survenue. Merci de réessayer."
@@ -163,9 +235,6 @@ msgstr "Êtes-vous sûr·e de vouloir supprimer ce pipeline programmé ?"
msgid "Are you sure you want to discard your changes?"
msgstr "Êtes-vous sûr·e de vouloir annuler vos modifications ?"
-msgid "Are you sure you want to leave this group?"
-msgstr "Êtes-vous sûr·e de vouloir quitter ce groupe ?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "Êtes-vous sûr·e de vouloir réinitialiser le jeton d’inscription ?"
@@ -178,6 +247,21 @@ msgstr "Êtes-vous certain ?"
msgid "Artifacts"
msgstr "Artéfacts"
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Attachez un fichier par glisser &amp; déposer ou %{upload_link}"
@@ -193,15 +277,18 @@ msgstr "Journal d'authentification"
msgid "Author"
msgstr "Auteur"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "Les applications de révision automatique et le déploiement automatique requièrent un nom de domaine et un %{kubernetes} pour fonctionner correctement."
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Les applications de révision automatique et de déploiement automatique requièrent un nom de domaine pour fonctionner correctement."
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "Les applications de révision automatique et de déploiement automatique requièrent un %{kubernetes} pour fonctionner correctement."
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "Auto DevOps (Béta)"
@@ -223,6 +310,12 @@ msgstr "Vous pouvez activer %{link_to_settings} pour ce projet."
msgid "Available"
msgstr "Disponible"
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr "Facturation"
@@ -277,6 +370,9 @@ msgstr "payé annuellement pour %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "par utilisateur"
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Branche"
@@ -405,8 +501,8 @@ msgstr "par"
msgid "CI / CD"
msgstr "Intégration continu / Déploiement continu"
-msgid "CI configuration"
-msgstr "Configuration de l'intégration continue (CI)"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr "Tâches"
@@ -417,6 +513,9 @@ msgstr "Annuler"
msgid "Cancel edit"
msgstr "Annuler modification"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr "Changer le poids"
@@ -432,15 +531,24 @@ msgstr "Picorer"
msgid "ChangeTypeAction|Revert"
msgstr "Défaire"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "Journal des modifications"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Statistiques"
msgid "Chat"
msgstr "Chat"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Vérification de la disponibilité de %{text}…"
@@ -453,8 +561,20 @@ msgstr "Picorer cette validation"
msgid "Cherry-pick this merge request"
msgstr "Picorer cette demande de fusion"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
-msgstr "Choisissez quels groupes vous souhaitez répliquer sur le nœud secondaire. Laissez vide pour tous les répliquer."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
msgid "CiStatusLabel|canceled"
msgstr "annulé"
@@ -510,224 +630,266 @@ msgstr "ignoré"
msgid "CiStatus|running"
msgstr "en cours"
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "CircuitBreaker API"
+msgid "Click to expand text"
+msgstr ""
+
msgid "Clone repository"
msgstr "Cloner le dépôt"
msgid "Close"
msgstr "Fermer"
-msgid "Cluster"
-msgstr "Cluster"
-
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
-msgstr "%{appList} a été installé avec succès sur votre cluster"
+msgid "Closed"
+msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
-msgstr "%{boldNotice} Cela va ajouter des ressources supplémentaires comme un répartiteur de charge, qui engendrent des coûts supplémentaires. Voir %{pricingLink}"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|API URL"
msgstr "URL de l'API"
-msgid "ClusterIntegration|Active"
-msgstr "Actif"
-
-msgid "ClusterIntegration|Add an existing cluster"
-msgstr "Ajouter un cluster existant"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Add cluster"
-msgstr "Ajoutez le cluster"
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|All"
-msgstr "Tous"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
msgid "ClusterIntegration|Applications"
msgstr "Applications"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificat d‘autorité de certification"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Paquet de l‘Autorité de certification (format PEM)"
-msgid "ClusterIntegration|Choose how to set up cluster integration"
-msgstr "Choisissez comment configurer l‘intégration de cluster"
-
-msgid "ClusterIntegration|Cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Cluster details"
-msgstr "Détails du cluster"
-
-msgid "ClusterIntegration|Cluster integration"
-msgstr "Intégration du cluster"
-
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "L'intégration du cluster est désactivée pour ce projet."
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "L'intégration du cluster est activée pour ce projet."
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "L'intégration de cluster est activée pour ce projet. La désactivation de cette intégration n’affectera pas votre cluster, il coupera temporairement la connexion de GitLab à celui-ci."
-
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
-msgstr "Nom du cluster"
-
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
-msgstr ""
+msgstr "Copier l’URL de l’API"
msgid "ClusterIntegration|Copy CA Certificate"
-msgstr ""
+msgstr "Copier le certificat CA"
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "Copier le nom du cluster"
+msgid "ClusterIntegration|Copy Token"
+msgstr "Copier le jeton"
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
-msgstr "Créer le cluster"
-
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "Activer l’intégration du cluster"
+msgid "ClusterIntegration|Create on GKE"
+msgstr "Créer sur GKE"
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr ""
+msgstr "Entrer les détails pour le cluster Kubernetes existant"
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
-msgstr ""
+msgstr "Éxécuteur GitLab"
msgid "ClusterIntegration|Google Cloud Platform project ID"
msgstr "ID de projet Google Cloud Platform"
msgid "ClusterIntegration|Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engine"
msgid "ClusterIntegration|Google Kubernetes Engine project"
-msgstr ""
+msgstr "Projet Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
+msgstr "Helm Tiller"
+
+msgid "ClusterIntegration|Ingress"
+msgstr "Ingress"
+
+msgid "ClusterIntegration|Install"
+msgstr "Installer"
+
+msgid "ClusterIntegration|Installed"
+msgstr "Installé"
+
+msgid "ClusterIntegration|Installing"
+msgstr "En cours d’installation"
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
msgstr ""
-msgid "ClusterIntegration|Inactive"
+msgid "ClusterIntegration|Integration status"
msgstr ""
-msgid "ClusterIntegration|Ingress"
+msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Install"
+msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
+msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Installed"
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
msgstr ""
-msgid "ClusterIntegration|Installing"
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "En savoir plus sur %{link_to_documentation}"
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr "Type de machine"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "Assurez-vous que votre compte %{link_to_requirements} pour créer des clusters"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "Gérer votre cluster en visitant le lien %{link_gke}"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
+msgid "ClusterIntegration|Note:"
+msgstr "Remarque :"
+
msgid "ClusterIntegration|Number of nodes"
msgstr "Nombre de nœuds"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Veuillez vous assurer que votre compte Google répond aux exigences suivantes : "
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr ""
-
msgid "ClusterIntegration|Project ID"
-msgstr ""
+msgstr "ID du projet"
msgid "ClusterIntegration|Project namespace"
-msgstr ""
+msgstr "Espace de noms du projet"
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "Espace de noms du projet (facultatif, unique)"
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
-msgstr "Lire notre %{link_to_help_page} sur l’intégration d’un cluster."
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
-msgstr "Retirer l’intégration du cluster"
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr "Retirer l’intégration"
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
-msgstr ""
+msgstr "La demande de lancement d'installation a échoué"
msgid "ClusterIntegration|Save changes"
-msgstr ""
+msgstr "Enregistrer les modifications"
-msgid "ClusterIntegration|See and edit the details for your cluster"
-msgstr "Voir et modifier les détails de votre cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|See machine types"
msgstr "Voir les types de machine"
@@ -739,55 +901,55 @@ msgid "ClusterIntegration|See zones"
msgstr "Voir les zones"
msgid "ClusterIntegration|Service token"
-msgstr ""
+msgstr "Jeton de service"
msgid "ClusterIntegration|Show"
-msgstr ""
+msgstr "Afficher"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "Un problème est survenu de notre côté."
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
-msgstr ""
+msgstr "Une erreur s’est produite lors de l'installation de %{title}"
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
-msgstr "Activer/désactiver le cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|Token"
-msgstr ""
+msgstr "Jeton"
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "Avec un cluster associé à ce projet, vous pouvez utiliser des applications de revue, déployer vos applications, exécuter vos pipelines et bien plus encore, de manière très simple."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
-msgstr ""
+msgstr "Votre compte doit disposer de %{link_to_kubernetes_engine}"
msgid "ClusterIntegration|Zone"
msgstr "Zone"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
-msgstr ""
+msgstr "Accèder à Google Kubernetes Engine"
-msgid "ClusterIntegration|cluster"
-msgstr "cluster"
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
msgid "ClusterIntegration|documentation"
-msgstr ""
+msgstr "documentation"
msgid "ClusterIntegration|help page"
msgstr "page d’aide"
msgid "ClusterIntegration|installing applications"
-msgstr ""
+msgstr "Installation des applications"
msgid "ClusterIntegration|meets the requirements"
msgstr "répond aux exigences"
@@ -795,6 +957,9 @@ msgstr "répond aux exigences"
msgid "ClusterIntegration|properly configured"
msgstr "correctement configuré"
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "Commentaires"
@@ -812,6 +977,9 @@ msgstr "Durée des 30 derniers pipelines en minutes"
msgid "Commit message"
msgstr "Message de validation"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Validation"
@@ -824,15 +992,57 @@ msgstr "Validations"
msgid "Commits feed"
msgstr "Flux de validations"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Historique"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Validé par"
msgid "Compare"
msgstr "Comparer"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr "Registre de conteneur"
@@ -884,20 +1094,23 @@ msgstr "Guide de contribution"
msgid "Contributors"
msgstr "Contributeurs"
-msgid "ContributorsPage|Building repository graph."
+msgid "ContributorsPage|%{startDate} – %{endDate}"
msgstr ""
+msgid "ContributorsPage|Building repository graph."
+msgstr "Construction du graphique du dépôt."
+
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
-msgstr ""
+msgstr "Validations sur %{branch_name}, à l’exclusion des validations de fusion. Limité à 6 000 validations."
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
-msgstr ""
+msgstr "Veuillez patienter, cette page va être automatiquement actualisée."
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr ""
+msgstr "Contrôler la concurrence maximale des remplacements de fichier-joint LFS pour ce nœud secondaire"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr ""
+msgstr "Contrôler la concurrence maximale des remplacements de dépôt pour ce nœud secondaire"
msgid "Copy SSH public key to clipboard"
msgstr "Copier la clé publique SSH dans le presse-papier"
@@ -905,9 +1118,18 @@ msgstr "Copier la clé publique SSH dans le presse-papier"
msgid "Copy URL to clipboard"
msgstr "Copier l'URL dans le presse-papier"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Copier le SHA de la validation"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Créer un nouveau dossier"
@@ -921,11 +1143,14 @@ msgid "Create empty bare repository"
msgstr "Créer un dépôt vide"
msgid "Create epic"
-msgstr ""
+msgstr "Créer l'épopée"
msgid "Create file"
msgstr "Créer un fichier"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Créer une demande de fusion"
@@ -938,6 +1163,9 @@ msgstr "Créer un nouveau dossier"
msgid "Create new file"
msgstr "Créer un nouveau fichier"
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Créer nouveau..."
@@ -951,7 +1179,7 @@ msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Créer un jeton d'accès personnel"
msgid "Creating epic"
-msgstr ""
+msgstr "Création de l'épopée en cours"
msgid "Cron Timezone"
msgstr "Fuseau horaire de Cron"
@@ -959,6 +1187,9 @@ msgstr "Fuseau horaire de Cron"
msgid "Cron syntax"
msgstr "Syntaxe Cron"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Événements de notification personnalisés"
@@ -968,9 +1199,6 @@ msgstr "Le niveau de notification Personnalisé est similaire au niveau Particip
msgid "Cycle Analytics"
msgstr "Analyseur de cycle"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "L’analyseur de cycle permet d’avoir une vue d’ensemble du temps nécessaire pour aller d’une idée à sa mise en production pour votre projet."
-
msgid "CycleAnalyticsStage|Code"
msgstr "Code"
@@ -1027,12 +1255,21 @@ msgstr "Les modèles de description permettent de définir des modèles spécifi
msgid "Details"
msgstr "Détails"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Nom du dossier"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "Supprimer les modifications"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Passer l’introduction Cycle Analytics"
@@ -1069,15 +1306,24 @@ msgstr "Diff simple"
msgid "DownloadSource|Download"
msgstr "Télécharger"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Éditer"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Éditer le pipeline programmé %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "Courriels"
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "Une erreur s‘est produite lors de la récupération des environnements."
@@ -1096,9 +1342,6 @@ msgstr "Environnement"
msgid "Environments|Environments"
msgstr "Environnements"
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr "Les environnements sont des lieux où le code est déployé, comme staging ou production."
-
msgid "Environments|Job"
msgstr "Tâche"
@@ -1130,20 +1373,44 @@ msgid "Environments|You don't have any environments right now."
msgstr "Vous n’avez aucun environnement pour le moment."
msgid "Epic will be removed! Are you sure?"
-msgstr ""
+msgstr "L’épopée sera supprimée ! Êtes-vous sûr•e ?"
msgid "Epics"
-msgstr ""
+msgstr "Épopées"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr ""
+msgstr "Les épopées vous permettent de gérer votre portefeuille de projets plus efficacement et avec moins d'effort"
msgid "Error creating epic"
+msgstr "Erreur lors de la création de l’épopée"
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
msgstr ""
msgid "Error occurred when toggling the notification subscription"
msgstr "Une erreur s’est produite lors de l’activation/désactivation de l’abonnement aux notifications"
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "Aucun filtre"
@@ -1171,6 +1438,9 @@ msgstr "Chaque mois (le 1er à 4:00 du matin)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Chaque semaine (dimanche à 4h00 du matin)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr "Explorer les projets"
@@ -1189,6 +1459,9 @@ msgstr "Févr."
msgid "February"
msgstr "Février"
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "Nom du fichier"
@@ -1233,29 +1506,113 @@ msgstr "Depuis la fusion de la demande de fusion jusqu'au déploiement en produc
msgid "GPG Keys"
msgstr "Clés GPG"
+msgid "Generate a default set of labels"
+msgstr ""
+
msgid "Geo Nodes"
msgstr "NÅ“uds Geo"
-msgid "GeoNodeSyncStatus|Failed"
-msgstr "Échec"
-
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr "Le nœud est défaillant ou cassé."
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr "Le nœud est lent, surchargé, ou il vient juste de récupérer après un problème."
-msgid "GeoNodeSyncStatus|Out of sync"
-msgstr "Désynchronisé"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
-msgstr "Synchronisé"
+msgid "Geo|All projects"
+msgstr ""
msgid "Geo|File sync capacity"
msgstr "Capacité de synchronisation de fichier"
-msgid "Geo|Groups to replicate"
-msgstr "Groupes à répliquer"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
msgid "Geo|Repository sync capacity"
msgstr "Capacité de synchronisation du dépôt"
@@ -1263,12 +1620,24 @@ msgstr "Capacité de synchronisation du dépôt"
msgid "Geo|Select groups to replicate."
msgstr "Sélectionner les groupes à répliquer."
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Les informations de santé du stockage Git ont été réinitialisées"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "Section de l'Exécuteur GitLab"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Aller à votre fourche"
@@ -1278,6 +1647,9 @@ msgstr "Fourche"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "L’authentification Google n’est pas %{link_to_documentation}. Demandez à votre administrateur GitLab si vous souhaitez utiliser ce service."
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Empêcher le partage d'un projet de %{group} avec d'autres groupes"
@@ -1314,8 +1686,8 @@ msgstr "Aucun groupe trouvé"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Vous pouvez gérer les autorisations des membres de votre groupe et accéder à chacun de ses projets."
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
-msgstr "Êtes-vous sûr·e de vouloir quitter le groupe « ${this.group.fullName} » ?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
+msgstr ""
msgid "GroupsTree|Create a project in this group."
msgstr "Créez un projet dans ce groupe."
@@ -1345,7 +1717,7 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "Désolé, aucun groupe ni projet ne correspond à vos critères de recherche"
msgid "Have your users email"
-msgstr ""
+msgstr "Lister les emails utilisateurs"
msgid "Health Check"
msgstr "État des services"
@@ -1365,6 +1737,11 @@ msgstr "Aucun problème détecté"
msgid "HealthCheck|Unhealthy"
msgstr "En mauvaise santé"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr "Historique"
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] "Instance"
msgstr[1] "Instances"
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Interne - Le groupe ainsi que tous les projets internes sont accessibles pour n’importe quel·le utilisa·teur·trice connecté·e."
@@ -1418,6 +1801,9 @@ msgstr "Tableaux"
msgid "Issues"
msgstr "Tickets"
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr "Janv."
@@ -1436,6 +1822,27 @@ msgstr "Juin"
msgid "June"
msgstr "Juin"
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Désactivé"
@@ -1445,6 +1852,9 @@ msgstr "Activé"
msgid "Labels"
msgstr "Labels"
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Le dernier %d jour"
@@ -1474,6 +1884,9 @@ msgstr "Vous avez poussé sur"
msgid "LastPushEvent|at"
msgstr "à"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "En apprendre plus dans le"
@@ -1492,14 +1905,18 @@ msgstr "Quitter le projet"
msgid "License"
msgstr "Licence"
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Limiter l'affichage au plus à %d évènement"
-msgstr[1] "Limiter l'affichage au plus à %d évènements"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr "Verrouiller"
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "Verrouillé"
@@ -1509,12 +1926,21 @@ msgstr "Fichiers verrouillés"
msgid "Login"
msgstr "Se connecter"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr "Mars"
msgid "March"
msgstr "Mars"
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "Nombre maximum d’échecs du stockage git"
@@ -1527,6 +1953,9 @@ msgstr "Médian"
msgid "Members"
msgstr "Membres"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr "Demandes de fusion"
@@ -1536,9 +1965,30 @@ msgstr "Événements de fusion"
msgid "Merge request"
msgstr "Demande de fusion"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "Messages"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "ajouter une clé SSH"
@@ -1548,17 +1998,29 @@ msgstr "Surveillance"
msgid "More information is available|here"
msgstr "ici"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr "Multiple tableaux de tickets"
-msgid "New Cluster"
-msgstr "Nouveau cluster"
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nouveau ticket"
msgstr[1] "Nouveaux tickets"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nouveau pipeline programmé"
@@ -1572,7 +2034,7 @@ msgid "New directory"
msgstr "Nouveau dossier"
msgid "New epic"
-msgstr ""
+msgstr "Nouvelle épopée"
msgid "New file"
msgstr "Nouveau fichier"
@@ -1583,6 +2045,9 @@ msgstr "Nouveau groupe"
msgid "New issue"
msgstr "Nouveau ticket"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Nouvelle demande de fusion"
@@ -1601,8 +2066,23 @@ msgstr "Nouveau sous-groupe"
msgid "New tag"
msgstr "Nouveau tag"
-msgid "No container images stored for this project. Add one by following the instructions above."
-msgstr "Aucune image de conteneur stockée pour ce projet. Ajoutez en une en suivant les instructions ci-dessous."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
msgid "No repository"
msgstr "Pas de dépôt"
@@ -1616,9 +2096,15 @@ msgstr "Pas de temps passé"
msgid "None"
msgstr "Aucun·e"
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "Indisponible"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "Données insuffisantes"
@@ -1679,6 +2165,12 @@ msgstr "Surveillé"
msgid "Notifications"
msgstr "Notifications"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr "Nov."
@@ -1688,8 +2180,8 @@ msgstr "Novembre"
msgid "Number of access attempts"
msgstr "Nombre de tentatives d'accès"
-msgid "Number of failures before backing off"
-msgstr "Nombre d'échecs avant annulation"
+msgid "OK"
+msgstr ""
msgid "Oct"
msgstr "Oct."
@@ -1703,6 +2195,9 @@ msgstr "Filtre"
msgid "Only project members can comment."
msgstr "Seuls les membres du projet peuvent commenter."
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr "Ouvert"
@@ -1736,9 +2231,6 @@ msgstr "« Première"
msgid "Password"
msgstr "Mot de Passe"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "Les personnes sans autorisation ne recevront jamais de notifications et ne pourront pas commenter."
-
msgid "Pipeline"
msgstr "Pipeline"
@@ -1781,12 +2273,6 @@ msgstr "Tous"
msgid "PipelineSchedules|Inactive"
msgstr "Inactif"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Nom de la variable"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Valeur de la variable"
-
msgid "PipelineSchedules|Next Run"
msgstr "Prochaine exécution"
@@ -1796,9 +2282,6 @@ msgstr "Aucune"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Indiquez une courte description"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Supprimer la variable"
-
msgid "PipelineSchedules|Take ownership"
msgstr "S’approprier"
@@ -1826,6 +2309,12 @@ msgstr "Pipelines de la semaine dernière"
msgid "Pipelines for last year"
msgstr "Pipelines de l’année dernière"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "Tous"
@@ -1838,12 +2327,21 @@ msgstr "avec l'étape"
msgid "Pipeline|with stages"
msgstr "avec les étapes"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "Veuillez résoudre le reCAPTCHA"
msgid "Preferences"
msgstr "Préférences"
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Privé - L’accès au projet doit être autorisé explicitement pour chaque utilisa·teur·trice."
@@ -1889,6 +2387,9 @@ msgstr "Votre compte est actuellement propriétaire des groupes suivants :"
msgid "Profiles|your account"
msgstr "votre compte"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Le projet “%{project_name}†est en train d’être supprimé."
@@ -1904,6 +2405,15 @@ msgstr "Projet '%{project_name}' mis à jour avec succès."
msgid "Project access must be granted explicitly to each user."
msgstr "L’accès au projet doit être explicitement accordé à chaque utilisateur."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "Détails du projet"
@@ -1922,6 +2432,21 @@ msgstr "L'export du projet a débuté. Un lien de téléchargement sera envoyé
msgid "ProjectActivityRSS|Subscribe"
msgstr "S’abonner"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Désactivé"
@@ -1946,15 +2471,9 @@ msgstr "Graphes"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "Contactez un administrateur pour modifier ce paramètre."
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr "Exécuter immédiatement un pipeline sur la branche par défaut"
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "Seules les validations signées peuvent être poussées sur ce dépôt."
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr "Problème lors de la configuration des paramètres CI/CD JavaScript"
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr "Ce paramètre est appliqué au niveau du serveur et peut être modifié par un administrateur."
@@ -1992,36 +2511,39 @@ msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr "Cette fonctionnalité requiert le support du localStorage par votre navigateur"
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
-msgstr ""
+msgstr "Par défaut, Prometheus écoute sur 'http://localhost:9090'. Il n’est pas recommandé de changer l’adresse et le port par défaut car cela pourrait affecter ou entrer en conflit avec d'autres services fonctionnant sur le serveur GitLab."
msgid "PrometheusService|Finding and configuring metrics..."
-msgstr ""
+msgstr "Recherche et configuration des métriques en cours…"
msgid "PrometheusService|Metrics"
-msgstr ""
+msgstr "Métriques"
msgid "PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters."
-msgstr ""
+msgstr "Les métriques sont automatiquement configurées et surveillées en fonction d’une bibliothèque de métriques provenant d’exportateurs populaires."
msgid "PrometheusService|Missing environment variable"
-msgstr ""
+msgstr "Variable d’environnement manquante"
msgid "PrometheusService|Monitored"
-msgstr ""
+msgstr "Surveillé"
msgid "PrometheusService|More information"
-msgstr ""
+msgstr "Plus d’informations"
msgid "PrometheusService|No metrics are being monitored. To start monitoring, deploy to an environment."
-msgstr ""
+msgstr "Aucune métrique n’est surveillée. Pour démarrer la surveillance, déployez sur un environnement."
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
-msgstr ""
+msgstr "URL de base de l’API Prometheus, comme http://prometheus.example.com/"
msgid "PrometheusService|Time-series monitoring service"
msgstr ""
msgid "PrometheusService|View environments"
+msgstr "Afficher les environnements"
+
+msgid "Protip:"
msgstr ""
msgid "Public - The group and any public projects can be viewed without any authentication."
@@ -2039,6 +2561,9 @@ msgstr "Évènements de poussée"
msgid "PushRule|Committer restriction"
msgstr "Restriction du validateur"
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Lire plus"
@@ -2051,6 +2576,12 @@ msgstr "Branches"
msgid "RefSwitcher|Tags"
msgstr "Tags"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr "Registre"
@@ -2075,9 +2606,18 @@ msgstr "Demandes fusionnées liées"
msgid "Remind later"
msgstr "Me le rappeler ultérieurement"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Supprimer le projet"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "Dépôt"
@@ -2093,6 +2633,11 @@ msgstr "Réinitialiser le jeton d’accès au bilan de santé"
msgid "Reset runners registration token"
msgstr "Réinitialiser le jeton d’inscription des exécuteurs"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "Défaire cette validation"
@@ -2102,15 +2647,15 @@ msgstr "Défaire cette demande de fusion"
msgid "SSH Keys"
msgstr "Clés SSH"
-msgid "Save"
-msgstr "Enregistrer"
-
msgid "Save changes"
msgstr "Enregistrer les modifications"
msgid "Save pipeline schedule"
msgstr "Sauvegarder le pipeline programmé"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Programmer un nouveau pipeline"
@@ -2121,43 +2666,64 @@ msgid "Scheduling Pipelines"
msgstr "Programmer des pipelines"
msgid "Scoped issue boards"
-msgstr ""
+msgstr "Tableaux de tickets avec portée limitée"
msgid "Search branches and tags"
msgstr "Rechercher dans les branches et les étiquettes"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "Nombre de secondes avant de réinitialiser les informations d’échec"
-msgid "Seconds to wait after a storage failure"
-msgstr "Nombre de secondes d'attente après un échec de stockage"
-
msgid "Seconds to wait for a storage access attempt"
msgstr "Nombre de secondes d’attente pour un essai d'accès au stockage"
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Sélectionnez le format de l'archive"
msgid "Select a timezone"
msgstr "Sélectionnez un fuseau horaire"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Sélectionnez une branche cible"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr "Sept."
msgid "September"
msgstr "Septembre"
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr "Modèles de service"
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Définissez un mot de passe pour votre compte pour pouvoir tirer ou pousser par %{protocol}."
-msgid "Set up CI"
-msgstr "Mettre en place l'intégration continue (CI)"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Mettre en place Koding"
@@ -2171,6 +2737,15 @@ msgstr "définir un mot de passe"
msgid "Settings"
msgstr "Paramètres"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr "Afficher les pages parentes"
@@ -2185,9 +2760,6 @@ msgstr[1] "Affichage de %d évènements"
msgid "Sidebar|Change weight"
msgstr "Changer le poids"
-msgid "Sidebar|Edit"
-msgstr "Modifier"
-
msgid "Sidebar|No"
msgstr "Non"
@@ -2200,18 +2772,30 @@ msgstr "Poids"
msgid "Snippets"
msgstr "Extraits de code"
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr "Une erreur est survenue de notre côté."
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr "Quelque chose ne s‘est pas bien passé en essayant de changer l’état de verrouillage de cette ${this.issuableDisplayName}"
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr "Une erreur s'est produite lors de la récupération des projets."
msgid "Something went wrong while fetching the registry list."
msgstr "Une erreur s'est produite lors de la récupération de la liste du registre."
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "Trier par"
@@ -2318,7 +2902,7 @@ msgid "Source code"
msgstr "Code source"
msgid "Source is not available"
-msgstr ""
+msgstr "La source n’est pas disponible"
msgid "Spam Logs"
msgstr "Journaux des messages indésirables"
@@ -2341,12 +2925,12 @@ msgstr "Démarrer l'Exécuteur !"
msgid "Stopped"
msgstr "Arrêté"
+msgid "Storage"
+msgstr ""
+
msgid "Subgroups"
msgstr "Sous-groupes"
-msgid "Subscribe"
-msgstr "S’abonner"
-
msgid "Switch branch/tag"
msgstr "Changer de branche / tag"
@@ -2442,8 +3026,11 @@ msgstr "Merci ! Ne plus afficher ce message"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "La Recherche Globale Avancée de Gitlab est un outils puissant qui vous fait gagner du temps. Au lieu de créer du code similaire et perdre du temps, vous pouvez maintenant chercher dans le code d'autres équipes pour vous aider sur votre projet."
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
-msgstr "Le seuil d’interruption du disjoncteur devrait être inférieur au seuil de nombre de défaillance"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "L’étape de développement montre le temps entre la première validation et la création de la demande de fusion. Les données seront automatiquement ajoutées ici une fois que vous aurez créé votre première demande de fusion."
@@ -2457,21 +3044,18 @@ msgstr "La relation de fourche a été supprimée."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "L'étape des tickets montre le temps nécessaire entre la création d'un ticket et son assignation à un jalon, ou son ajout à une liste d'un tableau de tickets. Commencez par créer des tickets pour voir des données pour cette étape."
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "The number of attempts GitLab will make to access a storage."
msgstr "Le nombre de tentatives que GitLab va effectuer pour accéder au stockage."
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
-msgstr "Le nombre d'échecs avant que GitLab ne commence à désactiver l'accès à la partition de stockage sur l'hôte"
-
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "Nombre d’échecs avant que GitLab n’empêche tout accès au stockage. Ce nombre d’échecs peut être réinitialisé dans l’interface d’administration : %{link_to_health_page} ou en suivant le %{api_documentation_link}."
msgid "The phase of the development lifecycle."
msgstr "Les étapes du cycle de développement."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "Les pipelines programmés exécutent des pipelines dans le futur, de façon répétée, pour les branches et tags spécifiées. Ces pipelines programmés héritent d’un accès partiel au projet basé sur l’utilisa·teur·trice qui leurs est associé·e."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "L’étape de planification montre le temps entre l’étape précédente et l’envoi de votre première validation. Ce temps sera automatiquement ajouté quand vous pousserez votre première validation."
@@ -2502,20 +3086,47 @@ msgstr "Délai en secondes pendant lequel GitLab gardera les informations d’é
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "Temps en secondes pendant lequel GitLab essaiera d’accéder au stockage. Après ce délai, une erreur d’expiration d’attente sera déclenchée."
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "Le temps pris par chaque entrée récoltée durant cette étape."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "La valeur située au point médian d’une série de valeur observée. C.à.d., entre 3, 5, 9, le médian est 5. Entre 3, 5, 7, 8, le médian est (5+7)/2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Il y a des difficultés à accéder aux données Git : "
-msgid "This board\\'s scope is reduced"
+msgid "There was an error loading users activity calendar."
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
-msgstr "Cette branche a changé depuis le début de l’édition. Souhaitez-vous créer une nouvelle branche ?"
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "This board\\'s scope is reduced"
+msgstr "La portée de ce tableau est limitée"
+
+msgid "This directory"
+msgstr ""
msgid "This is a confidential issue."
msgstr "Ce ticket est confidentiel."
@@ -2523,21 +3134,48 @@ msgstr "Ce ticket est confidentiel."
msgid "This is the author's first Merge Request to this project."
msgstr "C’est la première demande de fusion de cet auteur pour ce projet."
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr "Ce ticket est confidentiel et verrouillé."
msgid "This issue is locked."
msgstr "Ce ticket est verrouillé."
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Cela signifie que vous ne pouvez pas pousser du code tant que vous n’avez pas créé un dépôt vide, ou que vous n’avez pas importé un dépôt existant."
msgid "This merge request is locked."
msgstr "Cette demande de fusion est verrouillée."
-msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
msgstr ""
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr "Ces emails deviennent automatiquement des tickets (les commentaires étant extrait de la conversation par email) répertoriés ici."
+
msgid "Time before an issue gets scheduled"
msgstr "Temps avant qu’un ticket ne soit planifié"
@@ -2547,9 +3185,21 @@ msgstr "Temps avant que la résolution du ticket ne débute"
msgid "Time between merge request creation and merge/close"
msgstr "Temps entre la création d'une demande de fusion et sa fusion/clôture"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Temps jusqu’à la première demande de fusion"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "il y a %s jours"
@@ -2689,6 +3339,18 @@ msgstr "s"
msgid "Title"
msgstr "Titre"
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Temps total"
@@ -2702,22 +3364,43 @@ msgid "Track activity with Contribution Analytics."
msgstr "Suivre l’activité avec Contribution Analytics."
msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr "Suivez les groupes de tickets qui partagent un thème, entre projets et jalons"
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
msgstr ""
msgid "Turn on Service Desk"
+msgstr "Activer le Service Desk"
+
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr "Déverrouiller"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Déverrouillé"
msgid "Unstar"
msgstr "Supprimer des favoris"
-msgid "Unsubscribe"
-msgstr "Se désabonner"
+msgid "Up to date"
+msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Mettez à jour votre abonnement pour activer la Recherche Globale Avancée."
@@ -2740,11 +3423,14 @@ msgstr "Téléverser un nouveau fichier"
msgid "Upload file"
msgstr "Téléverser un fichier"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "Cliquez pour envoyer"
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr ""
+msgstr "Utilisez Service Desk pour intéragir avec vos utilisateurs (par exemple pour offrir un support client) par email directement dans GitLab"
msgid "Use the following registration token during setup:"
msgstr "Utiliser le jeton d’inscription suivant pendant l’installation :"
@@ -2752,9 +3438,15 @@ msgstr "Utiliser le jeton d’inscription suivant pendant l’installation :"
msgid "Use your global notification setting"
msgstr "Utiliser vos paramètres de notification globaux"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr "Voir le fichier @ "
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Afficher la demande de fusion"
@@ -2776,11 +3468,14 @@ msgstr "Inconnu"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Vous voulez voir les données ? Merci de contacter un administrateur pour en obtenir l’accès."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "Nous n'avons pas suffisamment de données pour afficher cette étape."
msgid "We want to be sure it is you, please confirm you are not a robot."
-msgstr ""
+msgstr "Nous voulons être sûrs que c'est bien vous, merci de confirmer que vous n’êtes pas un robot."
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
msgstr "Les webhooks vous permettent d’appeler une URL si, par exemple, du nouveau code est poussé ou un nouveau ticket est créé. Vous pouvez configurer les webhooks pour écouter les événements spécifiques comme des poussées de code, des tickets ou des demandes de fusion. Les webhooks de groupes s’appliqueront à tous les projets dans un groupe, ce qui vous permet de normaliser la fonctionnalité du webhook dans votre groupe entier."
@@ -2788,9 +3483,6 @@ msgstr "Les webhooks vous permettent d’appeler une URL si, par exemple, du nou
msgid "Weight"
msgstr "Poids"
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr "Si l’accès à un stockage échoue, GitLab empêchera l’accès au stockage pendant la durée spécifiée ici. Cela permettra au système de fichiers de récupérer. Les dépôts présents sur les secteurs en erreur seront temporairement indisponibles."
-
msgid "Wiki"
msgstr "Wiki"
@@ -2809,6 +3501,12 @@ msgstr "Il est recommandé d’installer %{markdown} pour que les spécificités
msgid "WikiClone|Start Gollum and edit locally"
msgstr "Démarrer Gollum et modifier localement"
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr "Vous n’êtes pas autorisé·e à créer des pages wiki"
@@ -2911,9 +3609,21 @@ msgstr "Vous allez supprimer la relation de fourche avec le projet source %{fork
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Vous allez transférer %{project_name_with_namespace} à un nouveau propriétaire. Êtes vous ABSOLUMENT sûr·e ?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Vous ne pouvez ajouter de fichier que dans une branche"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "Vous ne pouvez pas écrire sur une instance GitLab Geo secondaire en lecture-seule. Veuillez utiliser le %{link_to_primary_node} à la place."
@@ -2953,6 +3663,12 @@ msgstr "Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr "Vous ne pourrez pas récupérer ou pousser de code par SSH tant que vous n’aurez pas ajouté de clé SSH à votre profil"
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr "Votre commentaire ne sera pas visible publiquement."
@@ -2965,26 +3681,220 @@ msgstr "Votre nom"
msgid "Your projects"
msgstr "Vos projets"
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr "nom de la branche"
msgid "by"
msgstr "par"
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr "validation"
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "jour"
msgstr[1] "jours"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "nouvelle demande de fusion"
msgid "notification emails"
msgstr "courriels de notification"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "parent"
@@ -2996,12 +3906,21 @@ msgstr "mot de passe"
msgid "personal access token"
msgstr "jeton d’accès personnel"
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr "source"
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "pour aider vos contributeurs à communiquer efficacement !"
msgid "username"
msgstr "nom d’utilisateur"
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index fadc17a659d..889a03e7859 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -8,8 +8,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab 1.0.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-02-07 11:38-0600\n"
-"PO-Revision-Date: 2018-02-07 11:38-0600\n"
+"POT-Creation-Date: 2018-02-20 10:26+0100\n"
+"PO-Revision-Date: 2018-02-20 10:26+0100\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -150,6 +150,39 @@ msgstr ""
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "AdminProjects|Delete"
+msgstr ""
+
+msgid "AdminProjects|Delete Project %{projectName}?"
+msgstr ""
+
+msgid "AdminProjects|Delete project"
+msgstr ""
+
+msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
+msgid "AdminUsers|Block user"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username} and contributions?"
+msgstr ""
+
+msgid "AdminUsers|Delete User %{username}?"
+msgstr ""
+
+msgid "AdminUsers|Delete user"
+msgstr ""
+
+msgid "AdminUsers|Delete user and contributions"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{projectName}"
+msgstr ""
+
+msgid "AdminUsers|To confirm, type %{username}"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
@@ -177,9 +210,21 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
+msgid "An error occurred while importing project"
+msgstr ""
+
+msgid "An error occurred while loading commits"
+msgstr ""
+
+msgid "An error occurred while loading diff"
+msgstr ""
+
msgid "An error occurred while loading filenames"
msgstr ""
+msgid "An error occurred while loading the file"
+msgstr ""
+
msgid "An error occurred while rendering KaTeX"
msgstr ""
@@ -192,6 +237,9 @@ msgstr ""
msgid "An error occurred while retrieving diff"
msgstr ""
+msgid "An error occurred while saving assignees"
+msgstr ""
+
msgid "An error occurred while validating username"
msgstr ""
@@ -1018,6 +1066,9 @@ msgstr ""
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr ""
+msgid "Create branch"
+msgstr ""
+
msgid "Create directory"
msgstr ""
@@ -1033,6 +1084,9 @@ msgstr ""
msgid "Create merge request"
msgstr ""
+msgid "Create merge request and branch"
+msgstr ""
+
msgid "Create new branch"
msgstr ""
@@ -1290,9 +1344,15 @@ msgstr ""
msgid "Failed to change the owner"
msgstr ""
+msgid "Failed to remove issue from board, please try again."
+msgstr ""
+
msgid "Failed to remove the pipeline schedule"
msgstr ""
+msgid "Failed to update issues, please try again."
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -1985,6 +2045,24 @@ msgstr ""
msgid "Pipelines|Get started with Pipelines"
msgstr ""
+msgid "Pipeline|Retry pipeline"
+msgstr ""
+
+msgid "Pipeline|Retry pipeline #%{id}?"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline"
+msgstr ""
+
+msgid "Pipeline|Stop pipeline #%{id}?"
+msgstr ""
+
+msgid "Pipeline|You’re about to retry pipeline %{id}."
+msgstr ""
+
+msgid "Pipeline|You’re about to stop pipeline %{id}."
+msgstr ""
+
msgid "Pipeline|all"
msgstr ""
@@ -2144,12 +2222,30 @@ msgstr ""
msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr ""
+msgid "PrometheusService|Active"
+msgstr ""
+
+msgid "PrometheusService|Auto configuration"
+msgstr ""
+
+msgid "PrometheusService|Automatically deploy and configure Prometheus on your clusters to monitor your project’s environments"
+msgstr ""
+
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
msgstr ""
msgid "PrometheusService|Finding and configuring metrics..."
msgstr ""
+msgid "PrometheusService|Install Prometheus on clusters"
+msgstr ""
+
+msgid "PrometheusService|Manage clusters"
+msgstr ""
+
+msgid "PrometheusService|Manual configuration"
+msgstr ""
+
msgid "PrometheusService|Metrics"
msgstr ""
@@ -2171,9 +2267,18 @@ msgstr ""
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
msgstr ""
+msgid "PrometheusService|Prometheus is being automatically managed on your clusters"
+msgstr ""
+
msgid "PrometheusService|Time-series monitoring service"
msgstr ""
+msgid "PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters"
+msgstr ""
+
+msgid "PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below"
+msgstr ""
+
msgid "PrometheusService|View environments"
msgstr ""
@@ -2376,12 +2481,18 @@ msgstr ""
msgid "Something went wrong when toggling the button"
msgstr ""
+msgid "Something went wrong while closing the issue. Please try again later"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong while reopening the issue. Please try again later"
+msgstr ""
+
msgid "Something went wrong. Please try again."
msgstr ""
@@ -2478,6 +2589,9 @@ msgstr ""
msgid "Source"
msgstr ""
+msgid "Source (branch or tag)"
+msgstr ""
+
msgid "Source code"
msgstr ""
@@ -2738,6 +2852,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This page is unavailable because you are not allowed to read information across multiple projects."
+msgstr ""
+
msgid "This project"
msgstr ""
@@ -2934,9 +3051,6 @@ msgstr ""
msgid "Trigger this manual action"
msgstr ""
-msgid "Type %{value} to confirm:"
-msgstr ""
-
msgid "Unable to reset project cache."
msgstr ""
@@ -3229,6 +3343,9 @@ msgid_plural "merge requests"
msgstr[0] ""
msgstr[1] ""
+msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
+msgstr ""
+
msgid "mrWidget|Cancel automatic merge"
msgstr ""
@@ -3262,6 +3379,9 @@ msgstr ""
msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
msgstr ""
+msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line"
+msgstr ""
+
msgid "mrWidget|Mentions"
msgstr ""
@@ -3349,6 +3469,9 @@ msgstr ""
msgid "mrWidget|You can remove source branch now"
msgstr ""
+msgid "mrWidget|branch does not exist."
+msgstr ""
+
msgid "mrWidget|command line"
msgstr ""
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index 8b237dfe450..62a6da1604a 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:00-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Italian\n"
"Language: it_IT\n"
@@ -16,23 +16,41 @@ msgstr ""
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
msgstr[1] "%d commits"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d livello"
msgstr[1] "%d livelli"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s commit aggiuntivo è stato omesso per evitare degradi di prestazioni negli issues."
msgstr[1] "%s commit aggiuntivi sono stati omessi per evitare degradi di prestazioni negli issues."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} ha committato %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -45,9 +63,6 @@ msgstr "%{number_commits_behind} commits precedenti %{default_branch}, %{number_
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} di %{maximum_failures} fallimenti. GitLab consentirà l'accesso al prossimo tentativo."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} di %{maximum_failures} fallimenti. Gitlab bloccherà l'accesso per %{number_of_seconds} secondi."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} di %{maximum_failures} fallimenti. Gitlab non ritenterà automaticamente. Ripristina l'informazioni d'archiviazione quando il problema è risolto."
@@ -121,24 +136,81 @@ msgstr "Aggiungi Licenza"
msgid "Add new directory"
msgstr "Aggiungi una directory (cartella)"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "Pagina di stato"
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr "Impostazioni Avanzate"
msgid "All"
msgstr "Tutto"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr "Errore durante l'attivazione/disattivazione della sottoscrizione per l'iscrizione"
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr "Errore durante il recupero dei dati della barra laterale"
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr "Si è verificato un errore. Riprova."
@@ -163,9 +235,6 @@ msgstr "Sei sicuro di voler cancellare questa pipeline programmata?"
msgid "Are you sure you want to discard your changes?"
msgstr "Vuoi davvero ignorare le modifiche?"
-msgid "Are you sure you want to leave this group?"
-msgstr "Vuoi davvero lasciare questo gruppo?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "Sei sicuro di voler ripristinare il token di registrazione?"
@@ -178,6 +247,21 @@ msgstr "Sei sicuro?"
msgid "Artifacts"
msgstr "Artefatti"
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Aggiungi un file tramite trascina &amp; rilascia ( drag &amp; drop) o %{upload_link}"
@@ -193,15 +277,18 @@ msgstr "Log di autenticazione"
msgid "Author"
msgstr "Autore"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "Le app d'auto-review e l'Auto Deploy (rilascio automatico) necessita di un nome dominio e il servizio %{kubernetes} per funzionare correttamente."
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Le app d'auto-review e l'Auto Deploy (rilascio automatico) necessita di un nome dominio per funzionare correttamente."
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "Le app d'auto-review e l'Auto Deploy (rilascio automatico) necessita del servizio %{kubernetes} per funzionare correttamente."
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "Auto DevOps (Béta)"
@@ -223,6 +310,12 @@ msgstr "Puoi attivare %{link_to_settings} per questo progetto."
msgid "Available"
msgstr "Disponibile"
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -277,6 +370,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] ""
@@ -405,8 +501,8 @@ msgstr "per"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "Configurazione CI (Integrazione Continua)"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr "Jobs"
@@ -417,6 +513,9 @@ msgstr "Cancella"
msgid "Cancel edit"
msgstr "Annulla modifica"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -432,15 +531,24 @@ msgstr "Cherry-pick"
msgid "ChangeTypeAction|Revert"
msgstr "Ripristina"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "Changelog"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Grafici"
msgid "Chat"
msgstr "Chat"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Controllo disponibilità per %{text}…"
@@ -453,7 +561,19 @@ msgstr "Cherry-pick di questo commit"
msgid "Cherry-pick this merge request"
msgstr "Cherry-pick questa richiesta di merge"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -510,80 +630,92 @@ msgstr "saltata"
msgid "CiStatus|running"
msgstr "in corso"
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "api circuitbreaker"
+msgid "Click to expand text"
+msgstr ""
+
msgid "Clone repository"
msgstr "Clona repository"
msgid "Close"
msgstr ""
-msgid "Cluster"
-msgstr "Cluster"
-
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
-msgstr "%{appList} è stata installata con successo nel tuo cluster"
+msgid "Closed"
+msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
-msgstr "%{boldNotice} Ciò richiederà delle risorse extra come un load balancer, dove si applicheranno costi aggiuntivi. Consulta %{pricingLink}"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|API URL"
msgstr "API URL"
-msgid "ClusterIntegration|Active"
-msgstr "Attivo"
-
-msgid "ClusterIntegration|Add an existing cluster"
-msgstr "Aggiungi un cluster esistente"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Add cluster"
-msgstr "Aggiungi cluster"
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|All"
-msgstr "Tutti"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
msgid "ClusterIntegration|Applications"
msgstr "Applicazioni"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificato CA"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Certificate Authority bundle (formato PEM)"
-msgid "ClusterIntegration|Choose how to set up cluster integration"
-msgstr "Scegli come impostare l'integrazione cluster"
-
-msgid "ClusterIntegration|Cluster"
-msgstr "Cluster"
-
-msgid "ClusterIntegration|Cluster details"
-msgstr "Dettagli Cluster"
-
-msgid "ClusterIntegration|Cluster integration"
-msgstr "Integrazione Cluster"
-
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "L'integrazione dei cluster è disabilitata per questo progetto."
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "L'integrazione dei cluster è abilitata per questo progetto."
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "L'integrazione dei cluster è abilitata per questo progetto. Se la disabiliti il tuo cluster non sarà modificato, sarà solo spenta la connessione a Gitlab."
-
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
-msgstr "Nome Cluster"
-
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr "I cluster di consentono di revisionare le app, effettuare rilasci, eseguire pipelines, e molto altro in modo semplice. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "Copia URL API"
@@ -591,38 +723,35 @@ msgstr "Copia URL API"
msgid "ClusterIntegration|Copy CA Certificate"
msgstr "Copia Certificato CA"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
msgid "ClusterIntegration|Copy Token"
msgstr "Copia Token"
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "Copia nome del cluster"
-
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
-msgstr "Crea un nuovo cluster su Google Engine direttamente da Gitlab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Create cluster"
-msgstr "Crea cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr "Crea su GKE"
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "Abilita integrazione cluster"
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr "Inserisci i dettagli per un cluster Kubernetes esistente"
-msgid "ClusterIntegration|Enter the details for your cluster"
-msgstr "Inserisci i dettagli per il tuo cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Environment pattern"
-msgstr "Environment pattern"
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
-msgid "ClusterIntegration|GKE pricing"
-msgstr "Prezzi GKE"
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
@@ -639,47 +768,83 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
-msgid "ClusterIntegration|Inactive"
-msgstr "Inattivo"
-
msgid "ClusterIntegration|Ingress"
msgstr "Ingresso"
msgid "ClusterIntegration|Install"
msgstr "Installa"
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr "Installa applicazioni sul tuo cluster. Leggi di più a riguardo %{helpLink}"
-
msgid "ClusterIntegration|Installed"
msgstr "Installato"
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
-msgstr "Approfondisci riguardo i Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
+msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr "Tipo di macchina"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "Assicurati che il tuo account %{link_to_requirements} per creare i cluster"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
-msgstr "Gestisci l'integrazione dei cluster nel tuo progetto GitLab"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "Gestisci i tuoi cluster visitando %{link_gke}"
+msgid "ClusterIntegration|More information"
+msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
-msgstr "I cluster multipli sono disponibili nell'edizione Enterprise di Gitlab (Premium e Ultimate)"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
msgid "ClusterIntegration|Note:"
msgstr "Nota:"
@@ -687,18 +852,12 @@ msgstr "Nota:"
msgid "ClusterIntegration|Number of nodes"
msgstr "Numero di nodi"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr ""
-
msgid "ClusterIntegration|Project ID"
msgstr ""
@@ -708,16 +867,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,7 +888,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -747,25 +909,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -777,7 +939,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -795,6 +957,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "Commenti"
@@ -812,6 +977,9 @@ msgstr "Durata del commit (in minuti) per gli ultimi 30 commit"
msgid "Commit message"
msgstr "Messaggio del commit"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Commit"
@@ -824,15 +992,57 @@ msgstr "Commits"
msgid "Commits feed"
msgstr "Feed dei Commits"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Cronologia"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Committato da "
msgid "Compare"
msgstr "Confronta"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -884,6 +1094,9 @@ msgstr "Guida per contribuire"
msgid "Contributors"
msgstr "Collaboratori"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr "Genero grafico della repository."
@@ -905,9 +1118,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "Copia URL negli appunti"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Copia l'SHA del commit negli appunti"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Crea una nuova cartella"
@@ -926,6 +1148,9 @@ msgstr ""
msgid "Create file"
msgstr "Crea file"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Crea una richiesta di merge"
@@ -938,6 +1163,9 @@ msgstr "Crea una nuova cartella"
msgid "Create new file"
msgstr "Crea un nuovo File"
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Crea nuovo..."
@@ -959,6 +1187,9 @@ msgstr "Timezone del Cron"
msgid "Cron syntax"
msgstr "Sintassi Cron"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Eventi-Notifica personalizzati"
@@ -968,9 +1199,6 @@ msgstr "I livelli di notifica personalizzati sono uguali a quelli di partecipazi
msgid "Cycle Analytics"
msgstr "Statistiche Cicliche"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "L'Analisi Ciclica fornisce una panoramica sul tempo che trascorre tra l'idea ed il rilascio in produzione del tuo progetto"
-
msgid "CycleAnalyticsStage|Code"
msgstr "Codice"
@@ -1027,12 +1255,21 @@ msgstr ""
msgid "Details"
msgstr "Dettagli"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Nome cartella"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "Annulla modifiche"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Chiudi l'introduzione alle Analisi Cicliche"
@@ -1069,15 +1306,24 @@ msgstr "Differenze"
msgid "DownloadSource|Download"
msgstr "Scarica"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Modifica"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Cambia programmazione della pipeline %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "E-mail"
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "Errore durante il fetch degli ambienti."
@@ -1096,9 +1342,6 @@ msgstr "Ambiente"
msgid "Environments|Environments"
msgstr "Ambienti"
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr "Gli ambienti sono gli spazi dove il codice viene rilasciato, come staging o produzione."
-
msgid "Environments|Job"
msgstr "Job"
@@ -1141,9 +1384,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Errore durante l'attivazione/disattivazione della sottoscrizione per l'iscrizione"
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "Filtra per tutti"
@@ -1171,6 +1438,9 @@ msgstr "Ogni primo giorno del mese (alle 4 del mattino)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Ogni settimana (Di domenica alle 4 del mattino)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr "Esplora progetti"
@@ -1189,6 +1459,9 @@ msgstr "Feb"
msgid "February"
msgstr "Febbraio"
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "Nome file"
@@ -1233,10 +1506,10 @@ msgstr "Dalla richiesta di merge fino effettua il merge fino al rilascio in prod
msgid "GPG Keys"
msgstr "Chiavi GPG"
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1245,16 +1518,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1263,12 +1620,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Le informazioni sullo stato dell'archiviazione Git è stata ripristinata"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "Sezione Gitlab Runner"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Vai il tuo fork"
@@ -1278,6 +1647,9 @@ msgstr "Fork"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "L'autenticazione Google non è %{link_to_documentation}. Richiedi al tuo amministratore Gitlab se desideri utilizzare il servizio."
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Blocca la condivisione di un progetto di %{group} con altri gruppi"
@@ -1314,7 +1686,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1365,6 +1737,11 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr "Cronologia"
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1418,6 +1801,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr "Gen"
@@ -1436,6 +1822,27 @@ msgstr "Giu"
msgid "June"
msgstr "Giugno"
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Disabilitato"
@@ -1445,6 +1852,9 @@ msgstr "Abilitato"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "L'ultimo %d giorno"
@@ -1474,6 +1884,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Leggi di più su"
@@ -1492,14 +1905,18 @@ msgstr "Abbandona il progetto"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Limita visualizzazione %d d'evento"
-msgstr[1] "Limita visualizzazione %d di eventi"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "Bloccato"
@@ -1509,12 +1926,21 @@ msgstr ""
msgid "Login"
msgstr "Login"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr "Mar"
msgid "March"
msgstr "Marzo"
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1527,6 +1953,9 @@ msgstr "Mediano"
msgid "Members"
msgstr "Membri"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr "Richieste di merge"
@@ -1536,9 +1965,30 @@ msgstr ""
msgid "Merge request"
msgstr "Richiesta di merge"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "Messaggi"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "aggiungi una chiave SSH"
@@ -1548,17 +1998,29 @@ msgstr "Monitoraggio"
msgid "More information is available|here"
msgstr "Ulteriori informazioni sono disponibili | qui"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
-msgstr "Nuovo Cluster"
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nuovo Issue"
msgstr[1] "Nuovi Issues"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nuova pianificazione Pipeline"
@@ -1583,6 +2045,9 @@ msgstr "Nuovo gruppo"
msgid "New issue"
msgstr "Nuovo Issue"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Nuova richiesta di merge"
@@ -1601,7 +2066,22 @@ msgstr "Nuovo sottogruppo"
msgid "New tag"
msgstr "Nuovo tag"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1616,9 +2096,15 @@ msgstr ""
msgid "None"
msgstr "Nessuno"
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "Non disponibile"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "Dati insufficienti "
@@ -1679,6 +2165,12 @@ msgstr "Osserva"
msgid "Notifications"
msgstr "Notifiche"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr "Nov"
@@ -1688,7 +2180,7 @@ msgstr "Novembre"
msgid "Number of access attempts"
msgstr "Numero di tentativi di accesso raggiunto"
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1703,6 +2195,9 @@ msgstr "Filtra"
msgid "Only project members can comment."
msgstr "Solo i membri del progetto possono commentare."
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1736,9 +2231,6 @@ msgstr "« Prima"
msgid "Password"
msgstr "Password"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "Le persone che non hanno il permesso non saranno notificate e non potranno commentare."
-
msgid "Pipeline"
msgstr "Pipeline"
@@ -1781,12 +2273,6 @@ msgstr "Tutto"
msgid "PipelineSchedules|Inactive"
msgstr "Inattiva"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Chiave della variabile"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Valore della variabile"
-
msgid "PipelineSchedules|Next Run"
msgstr "Prossima esecuzione"
@@ -1796,9 +2282,6 @@ msgstr "Nessuna"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Fornisci una breve descrizione per questa pipeline"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Rimuovi riga della variabile"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Prendi possesso"
@@ -1826,6 +2309,12 @@ msgstr "Pipeline per la settimana scorsa"
msgid "Pipelines for last year"
msgstr "Pipeline per l'ultimo anno"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "tutto"
@@ -1838,12 +2327,21 @@ msgstr "con stadio"
msgid "Pipeline|with stages"
msgstr "con più stadi"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr "Preferenze"
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Privato - L'accesso al progetto deve essere fornito esplicitamente ad ogni utente."
@@ -1889,6 +2387,9 @@ msgstr "Il tuo account è attualmente proprietario in questi gruppi:"
msgid "Profiles|your account"
msgstr "il tuo account"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Il progetto '%{project_name}' è in fase di eliminazione."
@@ -1904,6 +2405,15 @@ msgstr "Il Progetto '%{project_name}' è stato aggiornato con successo."
msgid "Project access must be granted explicitly to each user."
msgstr "L'accesso al progetto dev'esser fornito esplicitamente ad ogni utente"
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "Dettagli del progetto"
@@ -1922,6 +2432,21 @@ msgstr "Esportazione del progetto iniziata. Un link di download sarà inviato vi
msgid "ProjectActivityRSS|Subscribe"
msgstr "Iscriviti"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Disabilitato"
@@ -1946,15 +2471,9 @@ msgstr "Grafico"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr "Esegui subito una pipeline sulla branch di default"
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr "Problemi durante l'impostazione delle CI/CD JavaScript settings"
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2024,6 +2543,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Pubblico - il gruppo e tutti i progetti pubblici possono essere visualizzati senza alcuna autenticazione."
@@ -2039,6 +2561,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Vedi altro"
@@ -2051,6 +2576,12 @@ msgstr "Branches"
msgid "RefSwitcher|Tags"
msgstr "Tags"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2075,9 +2606,18 @@ msgstr "Richieste di Merge Completate Correlate"
msgid "Remind later"
msgstr "Ricordamelo più tardi"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Rimuovi progetto"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2093,6 +2633,11 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "Ripristina questo commit"
@@ -2102,15 +2647,15 @@ msgstr "Ripristina questa richiesta di merge"
msgid "SSH Keys"
msgstr "Chiavi SSH"
-msgid "Save"
-msgstr "Salva"
-
msgid "Save changes"
msgstr "Salva modifiche"
msgid "Save pipeline schedule"
msgstr "Salva pianificazione pipeline"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Pianifica una nuova Pipeline"
@@ -2126,38 +2671,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "Ricerca branches e tags"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Seleziona formato d'archivio"
msgid "Select a timezone"
msgstr "Seleziona una timezone"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Seleziona una branch di destinazione"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr "Set"
msgid "September"
msgstr "Settembre"
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Establezca una contraseña en su cuenta para actualizar o enviar a través de %{protocol}."
-msgid "Set up CI"
-msgstr "Configura CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Configura Koding"
@@ -2171,6 +2737,15 @@ msgstr "imposta una password"
msgid "Settings"
msgstr "Impostazioni"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2185,9 +2760,6 @@ msgstr[1] "Visualizza %d eventi"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2200,18 +2772,30 @@ msgstr ""
msgid "Snippets"
msgstr "Snippet"
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr "Si è verificato un problema con il nostro server."
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr "Qualcosa è andato storto durante il fetch dei progetti."
msgid "Something went wrong while fetching the registry list."
msgstr "Qualcosa è andato storto durante il recupero dell'elenco dei registri."
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "Ordina per"
@@ -2341,10 +2925,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2442,7 +3026,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2457,10 +3044,10 @@ msgstr "La relazione del fork è stata rimossa"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Lo stadio di Issue mostra il tempo che impiega un issue ad esser correlato ad una Milestone, o ad esser aggiunto ad una tua Lavagna. Inizia la creazione di problemi per visualizzare i dati in questo stadio."
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2469,9 +3056,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "Il ciclo vitale della fase di sviluppo."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "Le pipelines pianificate vengono eseguite nel futuro, ripetitivamente, per specifici tag o branch ed ereditano restrizioni di progetto basate sull'utente ad esse associato."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Lo stadio di pianificazione mostra il tempo trascorso dal primo commit al suo step precedente. Questo periodo sarà disponibile automaticamente nel momento in cui farai il primo commit."
@@ -2502,19 +3086,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "Il tempo aggregato relativo eventi/data entry raccolto in quello stadio."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Il valore falsato nel mezzo di una serie di dati osservati. ES: tra 3,5,9 il mediano è 5. Tra 3,5,7,8 il mediano è (5+7)/2 quindi 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2523,18 +3134,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Questo significa che non è possibile effettuare push di codice fino a che non crei una repository vuota o ne importi una esistente"
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2547,9 +3185,21 @@ msgstr "Il tempo che impiega un issue per esser implementato"
msgid "Time between merge request creation and merge/close"
msgstr "Il tempo tra la creazione di una richiesta di merge ed il merge/close"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Il tempo fino alla prima richiesta di merge"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "%s giorni fa"
@@ -2689,6 +3339,18 @@ msgstr "s"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Tempo Totale"
@@ -2704,19 +3366,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr ""
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2740,6 +3423,9 @@ msgstr "Carica un nuovo file"
msgid "Upload file"
msgstr "Carica file"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "clicca per caricare"
@@ -2752,9 +3438,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr "Usa le tue impostazioni globali "
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Mostra la richieste di merge aperte"
@@ -2776,6 +3468,9 @@ msgstr "Sconosciuto"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Vuoi visualizzare i dati? Richiedi l'accesso ad un amministratore, grazie."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "Non ci sono sufficienti dati da mostrare su questo stadio"
@@ -2788,9 +3483,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2809,6 +3501,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2911,9 +3609,21 @@ msgstr "Stai per rimuovere la relazione con il progetto sorgente %{forked_from_p
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Stai per trasferire %{project_name_with_namespace} ad un altro owner. Sei ASSOLUTAMENTE sicuro?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Puoi aggiungere files solo quando sei in una branch"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2953,6 +3663,12 @@ msgstr "Non sarai in grado di effettuare push o pull tramite SSH fino a che %{ad
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2965,26 +3681,220 @@ msgstr "Il tuo nome"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "giorno"
msgstr[1] "giorni"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "Nuova richiesta di merge"
msgid "notification emails"
msgstr "Notifiche via email"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -2996,12 +3906,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index 700d70cad3f..31c4422c928 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:40-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:00-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Japanese\n"
"Language: ja_JP\n"
@@ -16,20 +16,35 @@ msgstr ""
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d個ã®ã‚³ãƒŸãƒƒãƒˆ"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "パフォーマンス低下をé¿ã‘ã‚‹ãŸã‚ %s 個ã®ã‚³ãƒŸãƒƒãƒˆã‚’çœç•¥ã—ã¾ã—ãŸã€‚"
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_timeago}ã«%{commit_author_link}ãŒã‚³ãƒŸãƒƒãƒˆã—ã¾ã—ãŸã€‚"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -41,9 +56,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
@@ -115,24 +127,81 @@ msgstr "ライセンスを追加"
msgid "Add new directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’追加"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr ""
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -157,9 +226,6 @@ msgstr "ã“ã®ãƒ‘イプラインスケジュールを削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Are you sure you want to discard your changes?"
msgstr ""
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -172,6 +238,21 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "ドラッグ&ドロップã¾ãŸã¯ %{upload_link} ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’添付"
@@ -187,13 +268,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -217,6 +301,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -271,6 +361,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "ブランãƒ"
@@ -398,8 +491,8 @@ msgstr "作者"
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
-msgstr "CI 設定"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -410,6 +503,9 @@ msgstr "キャンセル"
msgid "Cancel edit"
msgstr ""
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -425,15 +521,24 @@ msgstr "ãƒã‚§ãƒªãƒ¼ãƒ”ック"
msgid "ChangeTypeAction|Revert"
msgstr "リãƒãƒ¼ãƒˆ"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "変更履歴"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "ãƒãƒ£ãƒ¼ãƒˆ"
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -446,7 +551,19 @@ msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã‚’ãƒã‚§ãƒªãƒ¼ãƒ”ック"
msgid "Cherry-pick this merge request"
msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’ãƒã‚§ãƒªãƒ¼ãƒ”ック"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -503,79 +620,91 @@ msgstr "スキップ済ã¿"
msgid "CiStatus|running"
msgstr "実行中"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Applications"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -584,37 +713,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -632,64 +758,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -701,16 +857,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -719,7 +878,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -740,25 +899,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -770,7 +929,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -788,6 +947,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -804,6 +966,9 @@ msgstr "ç›´è¿‘30コミットã®æ‰€è¦æ™‚é–“(分)"
msgid "Commit message"
msgstr "コミットメッセージ"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "コミット"
@@ -816,15 +981,57 @@ msgstr "コミット"
msgid "Commits feed"
msgstr "コミットフィード"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "履歴"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "コミット担当者: "
msgid "Compare"
msgstr "比較"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -876,6 +1083,9 @@ msgstr "貢献者å‘ã‘ガイド"
msgid "Contributors"
msgstr "貢献者"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -897,9 +1107,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "クリップボードã«URLをコピー"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "コミットã®SHAをクリップボードã«ã‚³ãƒ”ー"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆ"
@@ -918,6 +1137,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "マージリクエストを作æˆ"
@@ -930,6 +1152,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "æ–°è¦ä½œæˆ"
@@ -951,6 +1176,9 @@ msgstr "Cron ã®ã‚¿ã‚¤ãƒ ã‚¾ãƒ¼ãƒ³"
msgid "Cron syntax"
msgstr "Cron ã®æ§‹æ–‡"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "カスタム通知設定"
@@ -960,9 +1188,6 @@ msgstr "\"カスタム\" ã®é€šçŸ¥ãƒ¬ãƒ™ãƒ«ã®åŸºæœ¬ã¯ \"å‚加\" ã¨åŒã˜ã§ã
msgid "Cycle Analytics"
msgstr "サイクル分æž"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "サイクル分æžã«ã‚ˆã‚Šã€ã‚ãªãŸã®ãƒ—ロジェクトãŒã‚¢ã‚¤ãƒ‡ã‚£ã‚¢ã®æ®µéšŽã‹ã‚‰ãƒ—ロダクション環境ã«ãƒªãƒªãƒ¼ã‚¹ã•ã‚Œã‚‹ã¾ã§ã©ã‚Œãらã„時間ãŒã‹ã‹ã£ãŸã‹ä¿¯çž°ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
-
msgid "CycleAnalyticsStage|Code"
msgstr "コード"
@@ -1018,12 +1243,21 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "ディレクトリå"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1060,15 +1294,24 @@ msgstr "プレーン差分"
msgid "DownloadSource|Download"
msgstr "ダウンロード"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "編集"
msgid "Edit Pipeline Schedule %{id}"
msgstr "パイプラインスケジュール %{id} を編集"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1087,9 +1330,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1132,9 +1372,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1162,6 +1426,9 @@ msgstr "毎月 (1æ—¥ã®åˆå‰4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "毎週 (日曜日ã®åˆå‰4:00)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1180,6 +1447,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1223,10 +1493,10 @@ msgstr "マージリクエストãŒãƒžãƒ¼ã‚¸ã•ã‚Œã¦ã‹ã‚‰ãƒ—ロダクション
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1235,16 +1505,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1253,12 +1607,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "自分ã®ãƒ•ã‚©ãƒ¼ã‚¯ã¸ç§»å‹•"
@@ -1268,6 +1634,9 @@ msgstr "フォーク"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1304,7 +1673,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1355,6 +1724,10 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+
msgid "History"
msgstr ""
@@ -1380,6 +1753,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1407,6 +1786,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1425,6 +1807,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "無効"
@@ -1434,6 +1837,9 @@ msgstr "有効"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "éŽåŽ»%d日間"
@@ -1462,6 +1868,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "詳ã—ã見る:"
@@ -1480,13 +1889,18 @@ msgstr "プロジェクトを離脱"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "イベント表示数を最大 %d 個ã«åˆ¶é™"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1496,12 +1910,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1514,6 +1937,9 @@ msgstr "中央値"
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1523,9 +1949,30 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "SSH éµã‚’追加"
@@ -1535,16 +1982,28 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "æ–°è¦èª²é¡Œ"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "æ–°è¦ãƒ‘イプラインスケジュール"
@@ -1569,6 +2028,9 @@ msgstr ""
msgid "New issue"
msgstr "æ–°è¦èª²é¡Œ"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "æ–°è¦ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
@@ -1587,7 +2049,22 @@ msgstr ""
msgid "New tag"
msgstr "æ–°è¦ã‚¿ã‚°"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1602,9 +2079,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "利用ã§ãã¾ã›ã‚“"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "データä¸è¶³"
@@ -1665,6 +2148,12 @@ msgstr "ã™ã¹ã¦é€šçŸ¥"
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1674,7 +2163,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1689,6 +2178,9 @@ msgstr "フィルター"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1722,9 +2214,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr "パイプライン"
@@ -1767,12 +2256,6 @@ msgstr "全件"
msgid "PipelineSchedules|Inactive"
msgstr "無効"
-msgid "PipelineSchedules|Input variable key"
-msgstr "変数ã®åå‰ã‚’入力"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "変数ã®å€¤ã‚’入力"
-
msgid "PipelineSchedules|Next Run"
msgstr "次ã®å®Ÿè¡Œ"
@@ -1782,9 +2265,6 @@ msgstr "ãªã—"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "ã“ã®ãƒ‘イプラインã«ã¤ã„ã¦ç°¡å˜ã«è¨˜è¿°ã—ã¦ãã ã•ã„。"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "変数を削除"
-
msgid "PipelineSchedules|Take ownership"
msgstr "権é™ã‚’å–å¾—ã™ã‚‹"
@@ -1812,6 +2292,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "全件"
@@ -1824,12 +2310,21 @@ msgstr "ステージã‚ã‚Š"
msgid "Pipeline|with stages"
msgstr "ステージã‚ã‚Š"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1875,6 +2370,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1890,6 +2388,15 @@ msgstr "'%{project_name}' プロジェクトã¯æ­£å¸¸ã«æ›´æ–°ã•ã‚Œã¾ã—ãŸã€‚
msgid "Project access must be granted explicitly to each user."
msgstr "ユーザーã”ã¨ã«ãƒ—ロジェクトアクセスã®æ¨©é™ã‚’指定ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1908,6 +2415,21 @@ msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始ã—ã¾ã—ãŸã€‚ダウン
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "無効"
@@ -1932,15 +2454,9 @@ msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‚°ãƒ©ãƒ•"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2010,6 +2526,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2025,6 +2544,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "続ãを読む"
@@ -2037,6 +2559,12 @@ msgstr "ブランãƒ"
msgid "RefSwitcher|Tags"
msgstr "ã‚¿ã‚°"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2061,9 +2589,18 @@ msgstr "関連ã™ã‚‹ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "Remind later"
msgstr "後ã§é€šçŸ¥"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "プロジェクトを削除"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2079,6 +2616,10 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+
msgid "Revert this commit"
msgstr "ã“ã®ã‚³ãƒŸãƒƒãƒˆã‚’リãƒãƒ¼ãƒˆ"
@@ -2088,15 +2629,15 @@ msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’リãƒãƒ¼ãƒˆ"
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "パイプラインスケジュールをä¿å­˜"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "æ–°ã—ã„パイプラインã®ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã‚’作æˆ"
@@ -2112,38 +2653,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "ブランãƒã¾ãŸã¯ã‚¿ã‚°ã‚’検索"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "アーカイブã®ãƒ•ã‚©ãƒ¼ãƒžãƒƒãƒˆã‚’é¸æŠž"
msgid "Select a timezone"
msgstr "タイムゾーンをé¸æŠž"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "ターゲットブランãƒã‚’é¸æŠž"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "%{protocol} プロコトル経由ã§ãƒ—ルã€ãƒ—ッシュã™ã‚‹ãŸã‚ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®ãƒ‘スワードを設定。"
-msgid "Set up CI"
-msgstr "CI を設定"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Koding を設定"
@@ -2157,6 +2719,15 @@ msgstr "パスワードを設定"
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2170,9 +2741,6 @@ msgstr[0] "%d ã®ã‚¤ãƒ™ãƒ³ãƒˆã‚’表示中"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2185,18 +2753,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2326,10 +2906,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2426,7 +3006,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2441,10 +3024,10 @@ msgstr "フォークã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "課題ステージã§ã¯ã€èª²é¡ŒãŒç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã‚‹ã‹ã€èª²é¡Œãƒœãƒ¼ãƒ‰ã®ãƒªã‚¹ãƒˆã«è¿½åŠ ã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚ã“ã®ãƒªã‚¹ãƒˆã«è¡¨ç¤ºã™ã‚‹ã«ã¯èª²é¡Œã‚’最åˆã«ä½œæˆã—ã¦ãã ã•ã„。"
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2453,9 +3036,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "開発ライフサイクルã®æ®µéšŽ"
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "パイプラインスケジュールã¯æŒ‡å®šã®ãƒ–ランãƒã¾ãŸã¯ã‚¿ã‚°ã«å¯¾ã—ã¦è‡ªå‹•çš„ã«ãƒ‘イプラインを実行ã—ã¾ã™ã€‚計画済ã¿ãƒ‘イプラインã¯ãれらã®ç´ä»˜ã‘られãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ロジェクトã¨åŒã˜æ¨©é™ã‚’継承ã—ã¾ã™ã€‚"
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "計画ステージã§ã¯ã€èª²é¡Œã‚¹ãƒ†ãƒ¼ã‚¸ã«ç™»éŒ²ã•ã‚Œã¦ã‹ã‚‰ãƒ—ッシュã•ã‚ŒãŸæœ€åˆã®ã‚³ãƒŸãƒƒãƒˆæ™‚刻ã¾ã§ã®æ™‚é–“ãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚最åˆã®ã‚³ãƒŸãƒƒãƒˆãŒãƒ—ッシュã•ã‚Œã¨ãã«è‡ªå‹•çš„ã«è¿½åŠ ã•ã‚Œã¾ã™ã€‚"
@@ -2486,19 +3066,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã«åŽé›†ã•ã‚ŒãŸãƒ‡ãƒ¼ã‚¿æ¯Žã®æ™‚é–“"
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "得られãŸä¸€é€£ã®ãƒ‡ãƒ¼ã‚¿ã‚’å°ã•ã„é †ã«ä¸¦ã¹ãŸã¨ãã«ä¸­å¤®ã«ä½ç½®ã™ã‚‹å€¤ã€‚例ãˆã°ã€3, 5, 9ã®ä¸­å¤®å€¤ã¯5。3, 5, 7, 8ã®ä¸­å¤®å€¤ã¯ (5+7)/2 = 6。"
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2507,18 +3114,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "空レãƒã‚¸ãƒˆãƒªãƒ¼ã‚’作æˆã¾ãŸã¯æ—¢å­˜ãƒ¬ãƒã‚¸ãƒˆãƒªãƒ¼ã‚’インãƒãƒ¼ãƒˆã‚’ã—ãªã‘ã‚Œã°ã€ã‚³ãƒ¼ãƒ‰ã®ãƒ—ッシュã¯ã§ãã¾ã›ã‚“。"
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2531,9 +3165,21 @@ msgstr "課題ã®å®Ÿè£…ãŒé–‹å§‹ã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“"
msgid "Time between merge request creation and merge/close"
msgstr "マージリクエストãŒä½œæˆã•ã‚Œã¦ã‹ã‚‰ãƒžãƒ¼ã‚¸ã¾ãŸã¯ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚Œã‚‹ã¾ã§ã®æ™‚é–“"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "最åˆã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¾ã§ã®æ™‚é–“"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "%sæ—¥å‰"
@@ -2671,6 +3317,18 @@ msgstr "秒"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "åˆè¨ˆæ™‚é–“"
@@ -2686,19 +3344,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "スターを外ã™"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2722,6 +3401,9 @@ msgstr "æ–°è¦ãƒ•ã‚¡ã‚¤ãƒ«ã‚’アップロード"
msgid "Upload file"
msgstr "ファイルをアップロード"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "クリックã—ã¦ã‚¢ãƒƒãƒ—ロード"
@@ -2734,9 +3416,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr "全体通知設定を利用"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "オープンãªãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’表示"
@@ -2758,6 +3446,9 @@ msgstr "ä¸æ˜Ž"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "ã“ã®ãƒ‡ãƒ¼ã‚¿ã‚’å‚ç…§ã—ãŸã„ã§ã™ã‹ï¼Ÿã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã«ã¯ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。"
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "データä¸è¶³ã®ãŸã‚ã€ã“ã®ã‚¹ãƒ†ãƒ¼ã‚¸ã®è¡¨ç¤ºã¯ã§ãã¾ã›ã‚“。"
@@ -2770,9 +3461,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2791,6 +3479,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2893,9 +3587,21 @@ msgstr "å…ƒã®ãƒ—ロジェクト (%{forked_from_project}) ã¨ã®ãƒªãƒ¬ãƒ¼ã‚·ãƒ§ã
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "%{project_name_with_namespace} プロジェクトを別ã®ã‚ªãƒ¼ãƒŠãƒ¼ã«ç§»è­²ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚本当ã«ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "ファイルを追加ã™ã‚‹ã«ã¯ã€ã©ã“ã‹ã®ãƒ–ランãƒã«ã„ãªã‘ã‚Œã°ã„ã‘ã¾ã›ã‚“"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2935,6 +3641,12 @@ msgstr "%{add_ssh_key_link} をプロファイルã«è¿½åŠ ã—ã¦ã„ãªã„ã®ã§ã
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2947,25 +3659,218 @@ msgstr "åå‰"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "æ—¥"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "æ–°è¦ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆ"
msgid "notification emails"
msgstr "メール通知"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "親"
@@ -2976,12 +3881,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index aab1650a0bc..73909e6f6de 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:41-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:00-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Korean\n"
"Language: ko_KR\n"
@@ -16,20 +16,35 @@ msgstr ""
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d 커밋"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s 추가 ì»¤ë°‹ì€ ì„±ëŠ¥ ì´ìŠˆë¥¼ 방지하기 위해 ìƒëžµë˜ì—ˆìŠµë‹ˆë‹¤."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_timeago} ì— %{commit_author_link} ë‹˜ì´ ì»¤ë°‹í•˜ì˜€ìŠµë‹ˆë‹¤. "
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -41,9 +56,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ ë‹¤ìŒ ì‹œë„ì—ì„œ 성공하면 ì ‘ê·¼ì„ í—ˆìš©í•  것입니다."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ %{number_of_seconds} ì´ˆ ê°„ ì ‘ê·¼ì„ ì œí•œí•˜ê² ìŠµë‹ˆë‹¤."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} / %{maximum_failures} 실패. GitLab ì€ ìžë™ìœ¼ë¡œ 다시 ì‹œë„하지 않습니다. 문제가 í•´ê²°ë˜ë©´ 저장 공간 정보를 초기화 해주세요. "
@@ -115,24 +127,81 @@ msgstr "ë¼ì´ì„ ìŠ¤ 추가"
msgid "Add new directory"
msgstr "새 디렉토리 추가"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr "ì „ì²´"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -157,9 +226,6 @@ msgstr "ì´ íŒŒì´í”„ë¼ì¸ ìŠ¤ì¼€ì¥´ì„ ì‚­ì œ 하시겠습니까?"
msgid "Are you sure you want to discard your changes?"
msgstr "변경 ë‚´ìš©ì„ ì·¨ì†Œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr "ë“±ë¡ í† í°ì„ 초기화 하시겠습니까?"
@@ -172,6 +238,21 @@ msgstr "확실합니까?"
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "드래그 &amp; 드롭 ë˜ëŠ” %{upload_link}"
@@ -187,13 +268,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -217,6 +301,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -271,6 +361,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "브랜치"
@@ -398,8 +491,8 @@ msgstr "작성ìž"
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
-msgstr "CI 설정"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -410,6 +503,9 @@ msgstr "취소"
msgid "Cancel edit"
msgstr ""
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -425,15 +521,24 @@ msgstr "Cherry-pick"
msgid "ChangeTypeAction|Revert"
msgstr "Revert"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "변경사항"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "차트"
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -446,7 +551,19 @@ msgstr "ì´ ì»¤ë°‹ì„ Cherry-pick"
msgid "Cherry-pick this merge request"
msgstr "ì´ ë¨¸ì§€ 리퀘스트를 Cherry-pick"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -503,79 +620,91 @@ msgstr "건너 뜀"
msgid "CiStatus|running"
msgstr "실행 중"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Applications"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -584,37 +713,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -632,64 +758,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -701,16 +857,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -719,7 +878,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -740,25 +899,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -770,7 +929,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -788,6 +947,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -804,6 +966,9 @@ msgstr "최근 30 ê±´ì˜ ì»¤ë°‹ 소요시간 (분)"
msgid "Commit message"
msgstr "커밋 메시지"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "커밋"
@@ -816,15 +981,57 @@ msgstr "커밋"
msgid "Commits feed"
msgstr "커밋 피드"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "ì´ë ¥"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "커밋한 사용ìž"
msgid "Compare"
msgstr "비êµ"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -876,6 +1083,9 @@ msgstr "ê¸°ì—¬ì— ëŒ€í•œ 안내"
msgid "Contributors"
msgstr "기여해 주신 분들"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -897,9 +1107,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "URLì„ í´ë¦½ë³´ë“œì— 복사"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "ì»¤ë°‹ì˜ SHA를 í´ë¦½ë³´ë“œë¡œ 복사합니다"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "새 디렉토리 만들기"
@@ -918,6 +1137,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "머지 리퀘스트 만들기"
@@ -930,6 +1152,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "새로 만들기 ..."
@@ -951,6 +1176,9 @@ msgstr "Cron 시간대"
msgid "Cron syntax"
msgstr "í¬ë¡  구문"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "ì‚¬ìš©ìž ì •ì˜ ì•Œë¦¼ ì´ë²¤íŠ¸"
@@ -960,9 +1188,6 @@ msgstr "ì‚¬ìš©ìž ì •ì˜ ì•Œë¦¼ ìˆ˜ì¤€ì€ ì°¸ì—¬ 수준과 ë™ì¼í•©ë‹ˆë‹¤. 맞ì
msgid "Cycle Analytics"
msgstr ""
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "Cycle Analytics는 프로ì íŠ¸ì—ì„œ ì•„ì´ë””어를 프로ë•ì…˜ìœ¼ë¡œ 옮기는 ë° ê±¸ë¦¬ëŠ” ì‹œê°„ì„ ëŒ€ëžµì ìœ¼ë¡œ ë³´ì—¬ì¤ë‹ˆë‹¤."
-
msgid "CycleAnalyticsStage|Code"
msgstr "코드"
@@ -1018,12 +1243,21 @@ msgstr ""
msgid "Details"
msgstr "ìƒì„¸"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "디렉토리 ì´ë¦„"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "변경 내용 취소"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1060,15 +1294,24 @@ msgstr "Plain Diff"
msgid "DownloadSource|Download"
msgstr "다운로드"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "편집"
msgid "Edit Pipeline Schedule %{id}"
msgstr "파ì´í”„ë¼ì¸ 스케줄 편집 %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1087,9 +1330,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1132,9 +1372,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "모든 ê°’ì„ ê¸°ì¤€ìœ¼ë¡œ í•„í„°"
@@ -1162,6 +1426,9 @@ msgstr "매월 (1ì¼ ì˜¤ì „ 4ì‹œ)"
msgid "Every week (Sundays at 4:00am)"
msgstr "매주 (ì¼ìš”ì¼ ì˜¤ì „ 4ì‹œì—)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1180,6 +1447,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1223,10 +1493,10 @@ msgstr "머지 리퀘스트 머지ì—ì„œ 프로ë•ì…˜ í™˜ê²½ì— ë°°í¬ê¹Œì§€"
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1235,16 +1505,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1253,12 +1607,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "git storage ìƒíƒœ ì •ë³´ê°€ 초기화ë˜ì—ˆìŠµë‹ˆë‹¤."
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "GitLab Runner 섹션"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "ë‹¹ì‹ ì˜ í¬í¬ë¡œ ì´ë™í•˜ì„¸ìš”"
@@ -1268,6 +1634,9 @@ msgstr "í¬í¬"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1304,7 +1673,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1355,6 +1724,10 @@ msgstr " 헬스 문제가 발견ë˜ì§€ 않았습니다."
msgid "HealthCheck|Unhealthy"
msgstr "비정ìƒ"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+
msgid "History"
msgstr ""
@@ -1380,6 +1753,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1407,6 +1786,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1425,6 +1807,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Disabled"
@@ -1434,6 +1837,9 @@ msgstr "Enabled"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "최근 %d ì¼"
@@ -1462,6 +1868,9 @@ msgstr "푸쉬: "
msgid "LastPushEvent|at"
msgstr "at"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "ë” ìžì„¸ížˆ 알아보기"
@@ -1480,13 +1889,18 @@ msgstr "프로ì íŠ¸ì—ì„œ 나가기"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "최대 %d ì´ë²¤íŠ¸ 만 표시하는 것으로 제한ë©ë‹ˆë‹¤."
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1496,12 +1910,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1514,6 +1937,9 @@ msgstr "중앙값"
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1523,9 +1949,30 @@ msgstr "머지 ì´ë²¤íŠ¸"
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "SSH 키 추가"
@@ -1535,16 +1982,28 @@ msgstr ""
msgid "More information is available|here"
msgstr "여기"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "새 ì´ìŠˆ"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "새로운 파ì´í”„ë¼ì¸ ì¼ì •"
@@ -1569,6 +2028,9 @@ msgstr ""
msgid "New issue"
msgstr "새 ì´ìŠˆ"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "새 머지 리퀘스트"
@@ -1587,7 +2049,22 @@ msgstr ""
msgid "New tag"
msgstr "새 태그 "
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1602,9 +2079,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "사용할 수 ì—†ìŒ"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "ë°ì´í„°ê°€ 충분하지 않습니다."
@@ -1665,6 +2148,12 @@ msgstr "Watch"
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1674,7 +2163,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1689,6 +2178,9 @@ msgstr "í•„í„°"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1722,9 +2214,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr "파ì´í”„ë¼ì¸"
@@ -1767,12 +2256,6 @@ msgstr "모ë‘"
msgid "PipelineSchedules|Inactive"
msgstr "비활성"
-msgid "PipelineSchedules|Input variable key"
-msgstr "입력 변수 키"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "입력 변수 값"
-
msgid "PipelineSchedules|Next Run"
msgstr "ë‹¤ìŒ ì‹¤í–‰"
@@ -1782,9 +2265,6 @@ msgstr "ì—†ìŒ"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "ì´ íŒŒì´í”„ë¼ì¸ì— 대한 간단한 설명 제공"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "변수 행 제거"
-
msgid "PipelineSchedules|Take ownership"
msgstr "소유권 가져 오기"
@@ -1812,6 +2292,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "모ë‘"
@@ -1824,12 +2310,21 @@ msgstr "스테ì´ì§•"
msgid "Pipeline|with stages"
msgstr "스테ì´ì§•"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1875,6 +2370,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1890,6 +2388,15 @@ msgstr "'%{project_name}'프로ì íŠ¸ê°€ 성공ì ìœ¼ë¡œ ì—…ë°ì´íŠ¸ë˜ì—ˆìŠµë‹
msgid "Project access must be granted explicitly to each user."
msgstr "프로ì íŠ¸ 액세스는 ê° ì‚¬ìš©ìžì—게 명시ì ìœ¼ë¡œ 부여ë˜ì–´ì•¼í•©ë‹ˆë‹¤."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "프로ì íŠ¸ ìƒì„¸"
@@ -1908,6 +2415,21 @@ msgstr "프로ì íŠ¸ 내보내기가 시작ë˜ì—ˆìŠµë‹ˆë‹¤. 다운로드 ë§í¬ë
msgid "ProjectActivityRSS|Subscribe"
msgstr "구ë…"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "사용 안 함"
@@ -1932,15 +2454,9 @@ msgstr "그래프"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2010,6 +2526,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2025,6 +2544,9 @@ msgstr "푸쉬 ì´ë²¤íŠ¸"
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "ë” ì½ê¸°"
@@ -2037,6 +2559,12 @@ msgstr "브랜치"
msgid "RefSwitcher|Tags"
msgstr "태그"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2061,9 +2589,18 @@ msgstr "관련 머지 리퀘스트"
msgid "Remind later"
msgstr "ë‚˜ì¤‘ì— ë‹¤ì‹œ 알림"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "프로ì íŠ¸ ì‚­ì œ"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2079,6 +2616,10 @@ msgstr "헬스 ì²´í¬ ì ‘ê·¼ í† í° ì´ˆê¸°í™”"
msgid "Reset runners registration token"
msgstr "runner ë“±ë¡ í† í° ì´ˆê¸°í™”"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+
msgid "Revert this commit"
msgstr "ì´ ì»¤ë°‹ ë˜ëŒë¦¬ê¸°"
@@ -2088,15 +2629,15 @@ msgstr "ì´ ë¨¸ì§€ 리퀘스트 ë˜ëŒë¦¬ê¸°"
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "파ì´í”„ë¼ì¸ 스케줄 저장"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "새로운 파ì´í”„ë¼ì¸ 스케줄 잡기"
@@ -2112,38 +2653,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "브랜치 ë° íƒœê·¸ 검색"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "ì•„ì¹´ì´ë¸Œ í¬ë§· ì„ íƒ"
msgid "Select a timezone"
msgstr "시간대 ì„ íƒ"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ ì„ íƒ"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "%{protocol} í”„ë¡œí† ì½œì„ í†µí•´ Pull 하거나 Push하려면 ê³„ì •ì— íŒ¨ìŠ¤ì›Œë“œë¥¼ 설정하십시오."
-msgid "Set up CI"
-msgstr "CI 설정"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Koding 설정"
@@ -2157,6 +2719,15 @@ msgstr "패스워드 설정"
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2170,9 +2741,6 @@ msgstr[0] "%d ê°œì˜ ì´ë²¤íŠ¸ 표시 중"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2185,18 +2753,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2326,10 +2906,10 @@ msgstr "Runner 시작!"
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2426,7 +3006,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2441,10 +3024,10 @@ msgstr "í¬í¬ 관계가 제거ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "ì´ìŠˆ 단계ì—는 ì´ìŠˆë¥¼ 작성하여 마ì¼ìŠ¤í†¤ìœ¼ë¡œ 지정하는 ë° ê±¸ë¦¬ëŠ” 시간 ë˜ëŠ” ì´ìŠˆ ë³´ë“œì˜ ëª©ë¡ì— ì´ìŠˆë¥¼ 추가하는 ì‹œê°„ì´ í‘œì‹œë©ë‹ˆë‹¤. ì´ ë‹¨ê³„ì˜ ë°ì´í„°ë¥¼ 보기 위해서는 ì´ìŠˆë¥¼ 먼저 작성해야 합니다."
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2453,9 +3036,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "개발 ìˆ˜ëª…ì£¼ê¸°ì˜ ë‹¨ê³„."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "파ì´í”„ë¼ì¸ ì¼ì •ì€ ë¯¸ëž˜ì— íŠ¹ì • 브랜치 ë˜ëŠ” íƒœê·¸ì— ëŒ€í•´ 반복ì ìœ¼ë¡œ 파ì´í”„ë¼ì¸ì„ 실행합니다. ì˜ˆì •ëœ íŒŒì´í”„ë¼ì¸ì€ 관련 사용ìžë¥¼ 기반으로 ì œí•œëœ í”„ë¡œì íŠ¸ 액세스 ê¶Œí•œì„ ìƒì†ë°›ìŠµë‹ˆë‹¤."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "ê³„íš ë‹¨ê³„ì—서는 ì´ì „ 단계ì—ì„œ 첫 번째 커밋 ì‹œê°„ì´ í‘œì‹œë©ë‹ˆë‹¤. ì´ ì‹œê°„ì€ ì²« 번째 ì»¤ë°‹ì„ ëˆ„ë¥´ë©´ ìžë™ìœ¼ë¡œ 추가ë©ë‹ˆë‹¤."
@@ -2486,19 +3066,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "해당 단계ì—ì„œ 수집 í•œ ê° ë°ì´í„° ìž…ë ¥ì— ì†Œìš” ëœ ì‹œê°„"
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "ê°’ì€ ì¼ë ¨ì˜ 관측 ê°’ 중ì ì— 있습니다. 예를 들어, 3, 5, 9 사ì´ì˜ 중간 ê°’ì€ 5입니다. 3, 5, 7, 8 사ì´ì˜ 중간 ê°’ì€ (5 + 7) / 2 = 6입니다."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "git storageì— ì ‘ê·¼í•˜ëŠ”ë° ë¬¸ì œê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. "
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2507,18 +3114,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "즉, 빈 저장소를 만들거나 기존 저장소를 가져올 때까지 코드를 Push 할 수 없습니다."
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2531,9 +3165,21 @@ msgstr "ì´ìŠˆê°€ 구현ë˜ê¸° ì „ì˜ ì‹œê°„"
msgid "Time between merge request creation and merge/close"
msgstr "머지 리퀘스트 ìƒì„±ê³¼ 머지 / 닫기 사ì´ì˜ 시간"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "첫 번째 머지 ë¦¬í€˜ìŠ¤íŠ¸ê¹Œì§€ì˜ ì‹œê°„"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "%s ì¼ ì „"
@@ -2671,6 +3317,18 @@ msgstr "ì´ˆ"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "시간 합계:"
@@ -2686,19 +3344,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "별표 제거"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2722,6 +3401,9 @@ msgstr "새 íŒŒì¼ ì—…ë¡œë“œ"
msgid "Upload file"
msgstr "íŒŒì¼ ì—…ë¡œë“œ"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "업로드하려면 í´ë¦­í•˜ì‹­ì‹œì˜¤."
@@ -2734,9 +3416,15 @@ msgstr "설정 ì¤‘ì— ë‹¤ìŒ ë“±ë¡ í† í° ì´ìš© : "
msgid "Use your global notification setting"
msgstr "전체 알림 설정 사용"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "열린 머지 리퀘스트보기"
@@ -2758,6 +3446,9 @@ msgstr "ì•Œ 수 ì—†ìŒ"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "ì´ ë°ì´í„°ë¥¼ ë³´ê³  싶ì€ê°€ìš”? 관리ìžì—게 액세스 ê¶Œí•œì„ ìš”ì²­í•˜ì„¸ìš”."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "ì´ ë‹¨ê³„ë¥¼ ë³´ì—¬ì£¼ê¸°ì— ì¶©ë¶„í•œ ë°ì´í„°ê°€ 없습니다."
@@ -2770,9 +3461,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2791,6 +3479,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2893,9 +3587,21 @@ msgstr "í¬í¬ 관계를 소스 프로ì íŠ¸ %{forked_from_project}ì— ëŒ€í•´ ì 
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "%{project_name_with_namespace}ì„ ë‹¤ë¥¸ 소유ìžì—게 ì´ì „하려고합니다. \"ì •ë§ë¡œ\" 확실합니까?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "ë¸Œëžœì¹˜ì— ìžˆì„ ë•Œì—만 파ì¼ì„ 추가 í•  수 있습니다."
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2935,6 +3641,12 @@ msgstr "ë‹¹ì‹ ì˜ í”„ë¡œí•„ì— %{add_ssh_key_link} 를 하기 ì „ì—는 SSH를 í
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2947,25 +3659,218 @@ msgstr "ê·€í•˜ì˜ ì´ë¦„"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "ì¼"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "새 머지 리퀘스트"
msgid "notification emails"
msgstr "알림 ì´ë©”ì¼"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "부모"
@@ -2976,12 +3881,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index eca1923fd2c..451be6434db 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:59-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Dutch\n"
"Language: nl_NL\n"
@@ -16,22 +16,40 @@ msgstr ""
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
msgstr[1] "%d commits"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
msgstr[1] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s andere commit is weggelaten om prestatieproblemen te voorkomen."
msgstr[1] "%s andere commits zijn weggelaten om prestatieproblemen te voorkomen."
-msgid "%{commit_author_link} committed %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
msgid "%{count} participant"
@@ -45,9 +63,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
@@ -121,24 +136,81 @@ msgstr "Licentie toevoegen"
msgid "Add new directory"
msgstr "Nieuwe map toevoegen"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr "Alles"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -163,9 +235,6 @@ msgstr ""
msgid "Are you sure you want to discard your changes?"
msgstr ""
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -178,6 +247,21 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
@@ -193,13 +277,16 @@ msgstr ""
msgid "Author"
msgstr "Auteur"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -223,6 +310,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -277,6 +370,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] ""
@@ -405,8 +501,8 @@ msgstr "door"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "CI Configuratie"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -417,6 +513,9 @@ msgstr "Annuleren"
msgid "Cancel edit"
msgstr "Bewerken annuleren"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -432,15 +531,24 @@ msgstr ""
msgid "ChangeTypeAction|Revert"
msgstr ""
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr ""
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Grafieken"
msgid "Chat"
msgstr "Chat"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -453,7 +561,19 @@ msgstr "Cherry-pick deze commit"
msgid "Cherry-pick this merge request"
msgstr ""
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -510,79 +630,91 @@ msgstr "overgeslagen"
msgid "CiStatus|running"
msgstr ""
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|Applications"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr ""
+
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -591,37 +723,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -639,64 +768,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -708,16 +867,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,7 +888,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -747,25 +909,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -777,7 +939,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -795,6 +957,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "Opmerkingen"
@@ -812,6 +977,9 @@ msgstr ""
msgid "Commit message"
msgstr ""
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Commit"
@@ -824,15 +992,57 @@ msgstr "Commits"
msgid "Commits feed"
msgstr ""
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Geschiedenis"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Gecommit door"
msgid "Compare"
msgstr "Vergelijk"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -884,6 +1094,9 @@ msgstr ""
msgid "Contributors"
msgstr ""
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -905,9 +1118,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr ""
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr ""
@@ -926,6 +1148,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr ""
@@ -938,6 +1163,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr ""
@@ -959,6 +1187,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -968,9 +1199,6 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr ""
-
msgid "CycleAnalyticsStage|Code"
msgstr "Code"
@@ -1027,12 +1255,21 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr ""
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1069,15 +1306,24 @@ msgstr ""
msgid "DownloadSource|Download"
msgstr ""
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr ""
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1096,9 +1342,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1141,9 +1384,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1171,6 +1438,9 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1189,6 +1459,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1233,10 +1506,10 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1245,16 +1518,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1263,12 +1620,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
@@ -1278,6 +1647,9 @@ msgstr ""
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1314,7 +1686,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1365,6 +1737,11 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr ""
@@ -1391,6 +1768,12 @@ msgid_plural "Instances"
msgstr[0] ""
msgstr[1] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1418,6 +1801,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1436,6 +1822,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -1445,6 +1852,9 @@ msgstr ""
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -1474,6 +1884,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -1492,14 +1905,18 @@ msgstr ""
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] ""
-msgstr[1] ""
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1509,12 +1926,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1527,6 +1953,9 @@ msgstr ""
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1536,9 +1965,30 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -1548,10 +1998,16 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
@@ -1559,6 +2015,12 @@ msgid_plural "New Issues"
msgstr[0] "Nieuwe issue"
msgstr[1] "Nieuwe issues"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr ""
@@ -1583,6 +2045,9 @@ msgstr ""
msgid "New issue"
msgstr ""
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr ""
@@ -1601,7 +2066,22 @@ msgstr ""
msgid "New tag"
msgstr ""
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1616,9 +2096,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr ""
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr ""
@@ -1679,6 +2165,12 @@ msgstr ""
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1688,7 +2180,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1703,6 +2195,9 @@ msgstr ""
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1736,9 +2231,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr ""
@@ -1781,12 +2273,6 @@ msgstr ""
msgid "PipelineSchedules|Inactive"
msgstr ""
-msgid "PipelineSchedules|Input variable key"
-msgstr ""
-
-msgid "PipelineSchedules|Input variable value"
-msgstr ""
-
msgid "PipelineSchedules|Next Run"
msgstr ""
@@ -1796,9 +2282,6 @@ msgstr ""
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr ""
-msgid "PipelineSchedules|Remove variable row"
-msgstr ""
-
msgid "PipelineSchedules|Take ownership"
msgstr ""
@@ -1826,6 +2309,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr ""
@@ -1838,12 +2327,21 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1889,6 +2387,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1904,6 +2405,15 @@ msgstr ""
msgid "Project access must be granted explicitly to each user."
msgstr ""
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1922,6 +2432,21 @@ msgstr ""
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr ""
@@ -1946,15 +2471,9 @@ msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2024,6 +2543,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2039,6 +2561,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr ""
@@ -2051,6 +2576,12 @@ msgstr ""
msgid "RefSwitcher|Tags"
msgstr ""
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2075,9 +2606,18 @@ msgstr ""
msgid "Remind later"
msgstr ""
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr ""
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2093,6 +2633,11 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr ""
@@ -2102,15 +2647,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr ""
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr ""
@@ -2126,37 +2671,58 @@ msgstr ""
msgid "Search branches and tags"
msgstr ""
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
msgid "Select a timezone"
msgstr ""
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr ""
-msgid "Set up CI"
+msgid "Set up CI/CD"
msgstr ""
msgid "Set up Koding"
@@ -2171,6 +2737,15 @@ msgstr ""
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2185,9 +2760,6 @@ msgstr[1] ""
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2200,18 +2772,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2341,10 +2925,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2442,7 +3026,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2457,10 +3044,10 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2469,9 +3056,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr ""
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -2502,19 +3086,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2523,18 +3134,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2547,9 +3185,21 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr ""
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr ""
@@ -2689,6 +3339,18 @@ msgstr "s"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -2704,19 +3366,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr ""
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2740,6 +3423,9 @@ msgstr ""
msgid "Upload file"
msgstr ""
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr ""
@@ -2752,9 +3438,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr ""
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -2776,6 +3468,9 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr ""
@@ -2788,9 +3483,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2809,6 +3501,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2911,9 +3609,21 @@ msgstr ""
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr ""
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr ""
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2953,6 +3663,12 @@ msgstr ""
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2965,26 +3681,220 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr ""
msgid "notification emails"
msgstr ""
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -2996,12 +3906,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index a9059b0a73b..9c5455eac67 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:41-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:01-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Polish\n"
"Language: pl_PL\n"
@@ -16,25 +16,46 @@ msgstr ""
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
-msgid "%{commit_author_link} committed %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr ""
msgid "%{count} participant"
@@ -49,9 +70,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr ""
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr ""
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr ""
@@ -127,24 +145,81 @@ msgstr ""
msgid "Add new directory"
msgstr ""
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr ""
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -169,9 +244,6 @@ msgstr ""
msgid "Are you sure you want to discard your changes?"
msgstr ""
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -184,6 +256,21 @@ msgstr ""
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr ""
@@ -199,13 +286,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -229,6 +319,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -283,6 +379,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] ""
@@ -412,7 +511,7 @@ msgstr ""
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
+msgid "CI/CD configuration"
msgstr ""
msgid "CICD|Jobs"
@@ -424,6 +523,9 @@ msgstr ""
msgid "Cancel edit"
msgstr ""
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -439,15 +541,24 @@ msgstr ""
msgid "ChangeTypeAction|Revert"
msgstr ""
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr ""
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -460,7 +571,19 @@ msgstr ""
msgid "Cherry-pick this merge request"
msgstr ""
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -517,79 +640,91 @@ msgstr ""
msgid "CiStatus|running"
msgstr ""
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -598,37 +733,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -646,64 +778,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -715,16 +877,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -733,7 +898,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -754,25 +919,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -784,7 +949,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -802,6 +967,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr ""
@@ -820,6 +988,9 @@ msgstr ""
msgid "Commit message"
msgstr ""
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr ""
@@ -832,15 +1003,57 @@ msgstr ""
msgid "Commits feed"
msgstr ""
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr ""
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr ""
msgid "Compare"
msgstr ""
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -892,6 +1105,9 @@ msgstr ""
msgid "Contributors"
msgstr ""
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -913,9 +1129,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr ""
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr ""
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr ""
@@ -934,6 +1159,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr ""
@@ -946,6 +1174,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr ""
@@ -967,6 +1198,9 @@ msgstr ""
msgid "Cron syntax"
msgstr ""
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr ""
@@ -976,9 +1210,6 @@ msgstr ""
msgid "Cycle Analytics"
msgstr ""
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr ""
-
msgid "CycleAnalyticsStage|Code"
msgstr ""
@@ -1036,12 +1267,21 @@ msgstr ""
msgid "Details"
msgstr ""
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr ""
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr ""
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1078,15 +1318,24 @@ msgstr ""
msgid "DownloadSource|Download"
msgstr ""
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr ""
msgid "Edit Pipeline Schedule %{id}"
msgstr ""
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1105,9 +1354,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1150,9 +1396,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr ""
@@ -1180,6 +1450,9 @@ msgstr ""
msgid "Every week (Sundays at 4:00am)"
msgstr ""
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1198,6 +1471,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1243,10 +1519,10 @@ msgstr ""
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1255,16 +1531,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1273,12 +1633,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr ""
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr ""
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr ""
@@ -1288,6 +1660,9 @@ msgstr ""
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1324,7 +1699,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1375,6 +1750,12 @@ msgstr ""
msgid "HealthCheck|Unhealthy"
msgstr ""
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "History"
msgstr ""
@@ -1402,6 +1783,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1429,6 +1816,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1447,6 +1837,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr ""
@@ -1456,6 +1867,9 @@ msgstr ""
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] ""
@@ -1486,6 +1900,9 @@ msgstr ""
msgid "LastPushEvent|at"
msgstr ""
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -1504,15 +1921,18 @@ msgstr ""
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1522,12 +1942,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1540,6 +1969,9 @@ msgstr ""
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1549,9 +1981,30 @@ msgstr ""
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -1561,10 +2014,16 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
@@ -1573,6 +2032,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr ""
@@ -1597,6 +2062,9 @@ msgstr ""
msgid "New issue"
msgstr ""
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr ""
@@ -1615,7 +2083,22 @@ msgstr ""
msgid "New tag"
msgstr ""
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1630,9 +2113,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr ""
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr ""
@@ -1693,6 +2182,12 @@ msgstr ""
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1702,7 +2197,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1717,6 +2212,9 @@ msgstr ""
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1750,9 +2248,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr ""
@@ -1795,12 +2290,6 @@ msgstr ""
msgid "PipelineSchedules|Inactive"
msgstr ""
-msgid "PipelineSchedules|Input variable key"
-msgstr ""
-
-msgid "PipelineSchedules|Input variable value"
-msgstr ""
-
msgid "PipelineSchedules|Next Run"
msgstr ""
@@ -1810,9 +2299,6 @@ msgstr ""
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr ""
-msgid "PipelineSchedules|Remove variable row"
-msgstr ""
-
msgid "PipelineSchedules|Take ownership"
msgstr ""
@@ -1840,6 +2326,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr ""
@@ -1852,12 +2344,21 @@ msgstr ""
msgid "Pipeline|with stages"
msgstr ""
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1903,6 +2404,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1918,6 +2422,15 @@ msgstr ""
msgid "Project access must be granted explicitly to each user."
msgstr ""
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr ""
@@ -1936,6 +2449,21 @@ msgstr ""
msgid "ProjectActivityRSS|Subscribe"
msgstr ""
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr ""
@@ -1960,15 +2488,9 @@ msgstr ""
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2038,6 +2560,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2053,6 +2578,9 @@ msgstr ""
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr ""
@@ -2065,6 +2593,12 @@ msgstr ""
msgid "RefSwitcher|Tags"
msgstr ""
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2089,9 +2623,18 @@ msgstr ""
msgid "Remind later"
msgstr ""
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr ""
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr ""
@@ -2107,6 +2650,12 @@ msgstr ""
msgid "Reset runners registration token"
msgstr ""
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Revert this commit"
msgstr ""
@@ -2116,15 +2665,15 @@ msgstr ""
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr ""
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr ""
@@ -2140,37 +2689,58 @@ msgstr ""
msgid "Search branches and tags"
msgstr ""
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr ""
msgid "Select a timezone"
msgstr ""
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr ""
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr ""
-msgid "Set up CI"
+msgid "Set up CI/CD"
msgstr ""
msgid "Set up Koding"
@@ -2185,6 +2755,15 @@ msgstr ""
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2200,9 +2779,6 @@ msgstr[2] ""
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2215,18 +2791,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2356,10 +2944,10 @@ msgstr ""
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2458,7 +3046,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2473,10 +3064,10 @@ msgstr ""
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr ""
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2485,9 +3076,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr ""
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr ""
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr ""
@@ -2518,19 +3106,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr ""
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr ""
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2539,18 +3154,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2563,9 +3205,21 @@ msgstr ""
msgid "Time between merge request creation and merge/close"
msgstr ""
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr ""
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr ""
@@ -2707,6 +3361,18 @@ msgstr ""
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr ""
@@ -2722,19 +3388,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr ""
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2758,6 +3445,9 @@ msgstr ""
msgid "Upload file"
msgstr ""
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr ""
@@ -2770,9 +3460,15 @@ msgstr ""
msgid "Use your global notification setting"
msgstr ""
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -2794,6 +3490,9 @@ msgstr ""
msgid "Want to see the data? Please ask an administrator for access."
msgstr ""
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr ""
@@ -2806,9 +3505,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2827,6 +3523,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2929,9 +3631,21 @@ msgstr ""
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr ""
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr ""
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2971,6 +3685,12 @@ msgstr ""
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2983,27 +3703,222 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr ""
msgid "notification emails"
msgstr ""
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] ""
@@ -3016,12 +3931,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index 24753f2b140..5aef8f45234 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:41-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:01-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Portuguese, Brazilian\n"
"Language: pt_BR\n"
@@ -16,23 +16,41 @@ msgstr ""
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
msgstr[1] "%d commits"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d camada"
msgstr[1] "%d camadas"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s commit adicional foi omitido para prevenir problemas de performance."
msgstr[1] "%s commits adicionais foram omitidos para prevenir problemas de performance."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} fez commit %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -45,9 +63,6 @@ msgstr "%{number_commits_behind} commits atrás de %{default_branch}, %{number_c
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} de %{maximum_failures} falhas. O GitLab permitirá o acesso na próxima tentativa."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} de %{maximum_failures} falhas. O GitLab irá bloquear o acesso por %{number_of_seconds} segundos."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} de %{maximum_failures} falhas. O GitLab não tentará mais automaticamente. Redefina as informações de storage quando o problema for resolvido."
@@ -121,24 +136,81 @@ msgstr "Adicionar Licença"
msgid "Add new directory"
msgstr "Adicionar novo diretório"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "página de saúde"
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr "Configurações avançadas"
msgid "All"
msgstr "Todos"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr "Erro ao modificar notificação de assinatura"
msgid "An error occurred when updating the issue weight"
msgstr "Um erro aconteceu ao atualizar o peso da issue"
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr "Erro ao recuperar informações da barra lateral"
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr "Ocorreu um erro. Tente novamente."
@@ -163,9 +235,6 @@ msgstr "Tem certeza que deseja excluir este agendamento de pipeline?"
msgid "Are you sure you want to discard your changes?"
msgstr "Você tem certeza que deseja descartar suas alterações?"
-msgid "Are you sure you want to leave this group?"
-msgstr "Tem certeza que quer sair desse grupo?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "Você tem certeza que quer recriar o token de registro?"
@@ -178,6 +247,21 @@ msgstr "Você tem certeza?"
msgid "Artifacts"
msgstr "Artefatos"
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Para anexar arquivo, arraste e solte ou %{upload_link}"
@@ -193,15 +277,18 @@ msgstr "Log de autenticação"
msgid "Author"
msgstr "Autor"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "Apps de revisão automática e Auto Deploy precisam de um nome de domínio e o %{kubernetes} para que funcione corretamente."
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Apps de revisão automática e Auto Deploy precisam de um nome de domínio para que funcione corretamente."
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "Apps de revisão automática e Auto Deploy precisam do %{kubernetes} para que funcione corretamente."
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "Auto DevOps (Beta)"
@@ -223,6 +310,12 @@ msgstr "Você pode ativar %{link_to_settings} para esse projeto."
msgid "Available"
msgstr "Disponível"
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr "Cobrança"
@@ -277,6 +370,9 @@ msgstr "pago %{price_per_year} anualmente"
msgid "BillingPlans|per user"
msgstr "por usuário"
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Branch"
@@ -370,13 +466,13 @@ msgid "Branches|To confirm, type %{branch_name_confirmation}:"
msgstr "Para confirmar, digite %{branch_name_confirmation}:"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr ""
+msgstr "Para descartar as mudanças locais e sobrescrever a branch com a versão de upstream, apague-o aqui e escolha 'Atualizar agora', acima."
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr "Você irá apagar irreparavelmente a branch protegida '%{branch_name}'."
msgid "Branches|diverged from upstream"
-msgstr ""
+msgstr "divergiu do upstream"
msgid "Branches|merged"
msgstr "merge realizado"
@@ -405,8 +501,8 @@ msgstr "por"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "Configuração da IC"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr "Jobs"
@@ -417,6 +513,9 @@ msgstr "Cancelar"
msgid "Cancel edit"
msgstr "Cancelar edição"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr "Alterar peso"
@@ -432,15 +531,24 @@ msgstr "Cherry-pick"
msgid "ChangeTypeAction|Revert"
msgstr "Reverter"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "Registro de mudanças"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Gráficos"
msgid "Chat"
msgstr "Bate-papo"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "Verificando disponibilidade de %{text}…"
@@ -453,8 +561,20 @@ msgstr "Cherry-pick esse commit"
msgid "Cherry-pick this merge request"
msgstr "Cherry-pick esse merge request"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
-msgstr "Escolha quais grupos você deseja replicar para este nó secundário. Deixe em branco para replicar tudo."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
msgid "CiStatusLabel|canceled"
msgstr "cancelado"
@@ -510,80 +630,92 @@ msgstr "ignorado"
msgid "CiStatus|running"
msgstr "executando"
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "interruptor da api"
+msgid "Click to expand text"
+msgstr ""
+
msgid "Clone repository"
msgstr "Clonar repositório"
msgid "Close"
msgstr "Fechar"
-msgid "Cluster"
-msgstr "Cluster"
-
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
-msgstr "%{appList} foi instalado no seu cluster"
+msgid "Closed"
+msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
-msgstr "%{boldNotice} isso irá adicionar recursos extras como balanceamento de carga, que acarretará em custos adicionais. Veja %{pricingLink}"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|API URL"
msgstr "API URL"
-msgid "ClusterIntegration|Active"
-msgstr "Ativo"
-
-msgid "ClusterIntegration|Add an existing cluster"
-msgstr "Adicionar um cluster existente"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Add cluster"
-msgstr "Adicionar cluster"
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|All"
-msgstr "Tudo"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
msgid "ClusterIntegration|Applications"
msgstr "Aplicações"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificado CA"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Pacote de autoridade certificadora (Formato PEM)"
-msgid "ClusterIntegration|Choose how to set up cluster integration"
-msgstr "Escolher como configurar a integração de cluster"
-
-msgid "ClusterIntegration|Cluster"
-msgstr "Cluster"
-
-msgid "ClusterIntegration|Cluster details"
-msgstr "Detalhes do cluster"
-
-msgid "ClusterIntegration|Cluster integration"
-msgstr "Integração do cluster"
-
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "Integração do cluster está desabilitada para esse projeto."
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "Integração do cluster está ativada nesse projeto."
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Integração do cluster está ativada para esse projeto. Desabilitar a integração não afetará seu cluster, mas desligará temporariamente a conexão do Gitlab com ele."
-
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
-msgstr "O Cluster está sendo criado no Google Kubernetes Engine..."
-
-msgid "ClusterIntegration|Cluster name"
-msgstr "Nome do cluster"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr "Clusters permitem que você utilize review apps, faça deploy de suas aplicações, rode pipelines, e muito mais de um jeito simples. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "Copiar URL da API"
@@ -591,38 +723,35 @@ msgstr "Copiar URL da API"
msgid "ClusterIntegration|Copy CA Certificate"
msgstr "Copiar certificado CA"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
msgid "ClusterIntegration|Copy Token"
msgstr "Copiar token"
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "Copiar nome do cluster"
-
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
-msgstr "Criar um novo cluster do Google Engine pelo GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Create cluster"
-msgstr "Criar cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr "Criar no GKE"
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "Ativar integração com o cluster"
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr "Insira os detalhes para o cluster Kubernetes existente"
-msgid "ClusterIntegration|Enter the details for your cluster"
-msgstr "Insira os detalhes para seu cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Environment pattern"
-msgstr "Padrão de ambiente"
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
-msgid "ClusterIntegration|GKE pricing"
-msgstr "Preços do GKE"
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr "Gitlab Runner"
@@ -631,55 +760,91 @@ msgid "ClusterIntegration|Google Cloud Platform project ID"
msgstr "ID do projeto no Google Cloud Platform"
msgid "ClusterIntegration|Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engine"
msgid "ClusterIntegration|Google Kubernetes Engine project"
-msgstr ""
+msgstr "Projeto do Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
-msgid "ClusterIntegration|Inactive"
-msgstr "Inativo"
-
msgid "ClusterIntegration|Ingress"
msgstr "Ingressar"
msgid "ClusterIntegration|Install"
msgstr "Instalar"
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr "Instalar aplicações no seu cluster. Leia mais em %{helpLink}"
-
msgid "ClusterIntegration|Installed"
msgstr "Instalado"
msgid "ClusterIntegration|Installing"
msgstr "Instalando"
-msgid "ClusterIntegration|Integrate cluster automation"
-msgstr "Integrar cluster de automação"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "Leia mais sobre %{link_to_documentation}"
-msgid "ClusterIntegration|Learn more about Clusters"
-msgstr "Ler mais sobre clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
+msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr "Tipo de máquina"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "Confira se sua conta %{link_to_requirements} para criar clusters"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
-msgstr "Gerenciar cluster de integração no projeto do GitLab"
+msgid "ClusterIntegration|Manage"
+msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "Gerencie seu cluster visitando %{link_gke}"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
+
+msgid "ClusterIntegration|More information"
+msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
-msgstr "Múltiplos clusters estão disponíveis no GitLab Enterprise Premium e Ultimate"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
msgid "ClusterIntegration|Note:"
msgstr "Nota:"
@@ -687,18 +852,12 @@ msgstr "Nota:"
msgid "ClusterIntegration|Number of nodes"
msgstr "Número de nós"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
-msgstr "Por favor, insira informações de acesso para seu cluster. Se precisar de ajuda, você pode ler %{link_to_help_page} em cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Por favor, tenha certeza que sua conta no Google cumpre com os requisitos:"
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr "Problema ao configurar o cluster"
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr "Problema ao configurar a lista de cluster"
-
msgid "ClusterIntegration|Project ID"
msgstr "ID do projeto"
@@ -708,16 +867,19 @@ msgstr "Namespace do projeto"
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "Namespace do projeto (opcional, único)"
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
-msgstr "Ler nossa %{link_to_help_page} na integração com cluster."
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
+
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
-msgstr "Remover integração com cluster"
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr "Remover integração"
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -726,8 +888,8 @@ msgstr "Solicitação para início de instalação falhou"
msgid "ClusterIntegration|Save changes"
msgstr "Salvar alterações"
-msgid "ClusterIntegration|See and edit the details for your cluster"
-msgstr "Ver e editar os detalhes para seu cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|See machine types"
msgstr "Ver tipos de máquina"
@@ -747,38 +909,38 @@ msgstr "Mostrar"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "Alguma coisa deu errado do nosso lado."
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Algo deu errado ao instalar %{title}"
-msgid "ClusterIntegration|There are no clusters to show"
-msgstr "Não há clusters para mostrar"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
-msgstr "Essa conta precisa de permissão para criar um cluster no %{link_to_container_project} especificado."
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
-msgstr "Alternar cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|Token"
msgstr "Token"
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "Com um cluster associado à esse projeto, você pode usar revisão de apps, fazer deploy de suas aplicações, rodar suas pipelines e muito mais de um jeito simples."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
-msgstr ""
+msgstr "Sua conta precisa de %{link_to_kubernetes_engine}"
msgid "ClusterIntegration|Zone"
msgstr "Zona"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
-msgstr ""
+msgstr "acesso ao Google Container Engine"
-msgid "ClusterIntegration|cluster"
-msgstr "cluster"
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
msgid "ClusterIntegration|documentation"
msgstr "documentação"
@@ -795,6 +957,9 @@ msgstr "atende aos requisitos"
msgid "ClusterIntegration|properly configured"
msgstr "configurado corretamente"
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "Comentários"
@@ -812,6 +977,9 @@ msgstr "Duração do commit em minutos para os últimos 30 commits"
msgid "Commit message"
msgstr "Mensagem de commit"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Commit"
@@ -824,15 +992,57 @@ msgstr "Commits"
msgid "Commits feed"
msgstr "Feed de commits"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "Histórico"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Commit feito por"
msgid "Compare"
msgstr "Comparar"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr "Container Registry"
@@ -884,6 +1094,9 @@ msgstr "Guia de contribuição"
msgid "Contributors"
msgstr "Contribuidores"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr "Gerando gráfico do repositório."
@@ -894,20 +1107,29 @@ msgid "ContributorsPage|Please wait a moment, this page will automatically refre
msgstr "Por favor, espere um momento, essa página será atualizada automaticamente."
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr ""
+msgstr "Controle a concorrência máxima de LFS/preenchimento de repositórios para esse nó secundário"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr ""
+msgstr "Controle a concorrência máxima de preenchimento de repositório para esse nó secundário"
msgid "Copy SSH public key to clipboard"
-msgstr ""
+msgstr "Copiar chave públic SSH para área de transferência"
msgid "Copy URL to clipboard"
msgstr "Copiar URL para área de transferência"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Copiar SHA do commit para a área de transferência"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Criar Novo Diretório"
@@ -926,6 +1148,9 @@ msgstr "Criar épico"
msgid "Create file"
msgstr "Criar arquivo"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Criar merge request"
@@ -938,6 +1163,9 @@ msgstr "Criar nova pasta"
msgid "Create new file"
msgstr "Criar novo arquivo"
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Criar novo..."
@@ -959,6 +1187,9 @@ msgstr "Fuso horário do cron"
msgid "Cron syntax"
msgstr "Sintaxe do cron"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Eventos de notificação personalizados"
@@ -968,9 +1199,6 @@ msgstr "Níveis de notificação personalizados são equivalentes a níveis de p
msgid "Cycle Analytics"
msgstr "Análise de Ciclo"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "A Análise de Ciclo fornece uma visão geral de quanto tempo uma ideia demora para ir para produção em seu projeto."
-
msgid "CycleAnalyticsStage|Code"
msgstr "Código"
@@ -1027,12 +1255,21 @@ msgstr "Modelos de descrição permitem que você defina modelos de contextos es
msgid "Details"
msgstr "Detalhes"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Nome do diretório"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "Descartar alterações"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Ignorar introdução do Cycle Analytics"
@@ -1069,15 +1306,24 @@ msgstr "Arquivo de texto com as mudanças"
msgid "DownloadSource|Download"
msgstr "Baixar"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Alterar"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Alterar Agendamento do Pipeline %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "Emails"
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "Um erro ocorreu ao recuperar ambientes."
@@ -1096,9 +1342,6 @@ msgstr "Ambiente"
msgid "Environments|Environments"
msgstr "Ambientes"
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr "Ambientes são lugares onde códigos são implantados (deploy), como homologação ou produção."
-
msgid "Environments|Job"
msgstr "Job"
@@ -1141,9 +1384,33 @@ msgstr "Epics permite que você gerencie seu portfólio de projetos de forma mai
msgid "Error creating epic"
msgstr "Erro ao criar épico"
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "Erro ao alterar configuração de notificação de assinatura"
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "EventFilterBy|Filtrar por tudo"
@@ -1171,6 +1438,9 @@ msgstr "Todos os meses (no dia primeiro às 4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Toda semana (domingos às 4:00)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr "Explorar projetos"
@@ -1189,6 +1459,9 @@ msgstr "Fev"
msgid "February"
msgstr "Fevereiro"
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "Nome do arquivo"
@@ -1233,42 +1506,138 @@ msgstr "Do merge request até a implantação em produção"
msgid "GPG Keys"
msgstr "Chaves GPG"
+msgid "Generate a default set of labels"
+msgstr ""
+
msgid "Geo Nodes"
msgstr "Nós de geo"
-msgid "GeoNodeSyncStatus|Failed"
-msgstr "Falhou"
-
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr "Nó está falhando ou quebrado."
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr "Nó está lento, sobrecarregado, ou acabou de recuperar após uma interrupção."
-msgid "GeoNodeSyncStatus|Out of sync"
-msgstr "Sem sincronia"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
-msgstr "Sincronizado"
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
msgid "Geo|File sync capacity"
msgstr "Capacidade de sincronização de arquivos"
-msgid "Geo|Groups to replicate"
-msgstr "Grupos para replicação"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
msgid "Geo|Repository sync capacity"
msgstr "Capacidade de sincronização do repositório"
msgid "Geo|Select groups to replicate."
+msgstr "Selecione grupos para replicar."
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
msgstr ""
msgid "Git storage health information has been reset"
msgstr "Informações sobre o status de saúde do storage Git foram reiniciadas"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "Seção GitLab Runner"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Ir para seu fork"
@@ -1278,6 +1647,9 @@ msgstr "Fork"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Autenticação do Google não está %{link_to_documentation}. Peça ao administrador do Gitlab se você deseja usar esse serviço."
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Bloquear compartilhamento de projetos do grupo %{group} com outros grupos"
@@ -1314,8 +1686,8 @@ msgstr "Nenhum grupo encontrado"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Você pode gerenciar permissões de membros e acesso do seu grupo para cada projeto no grupo."
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
-msgstr "Você tem certeza que deseja sair do grupo \"${this.group.fullName}\"?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
+msgstr ""
msgid "GroupsTree|Create a project in this group."
msgstr "Criar um projeto nesse grupo."
@@ -1345,7 +1717,7 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "Desculpe, nenhum grupo ou projeto correspondem à sua pesquisa"
msgid "Have your users email"
-msgstr ""
+msgstr "E-mail para abertura de issues"
msgid "Health Check"
msgstr "Status de Saúde"
@@ -1365,6 +1737,11 @@ msgstr "Nenhum problema de saúde detectado"
msgid "HealthCheck|Unhealthy"
msgstr "Não saudável"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "History"
msgstr "Histórico"
@@ -1375,21 +1752,27 @@ msgid "Import repository"
msgstr "Importar repositório"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr ""
+msgstr "Melhorar issue boards com o GitLab Enterprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Melhore a gerência de issues com pesos no GitLab Enterprise Edition."
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Encontre o que precisa mais facilmente com a pesquisa global avançada com GitLab Enterprise Edition."
msgid "Install a Runner compatible with GitLab CI"
msgstr "Instalar um Runner compatível com o GitLab CI"
msgid "Instance"
msgid_plural "Instances"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Instância"
+msgstr[1] "Instâncias"
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Interno - O grupo e projetos internos podem ser visualizados por qualquer usuário autenticado."
@@ -1404,7 +1787,7 @@ msgid "Introducing Cycle Analytics"
msgstr "Apresentando a Análise de Ciclo"
msgid "Issue board focus mode"
-msgstr ""
+msgstr "Focus mode no issue board"
msgid "Issue events"
msgstr "Eventos de issue"
@@ -1413,11 +1796,14 @@ msgid "IssueBoards|Board"
msgstr "Board"
msgid "IssueBoards|Boards"
-msgstr ""
+msgstr "Boards"
msgid "Issues"
msgstr "Issues"
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr "Jan"
@@ -1436,6 +1822,27 @@ msgstr "Jun"
msgid "June"
msgstr "Junho"
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Desabilitado"
@@ -1445,6 +1852,9 @@ msgstr "Habilitado"
msgid "Labels"
msgstr "Etiquetas"
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "Último %d dia"
@@ -1474,6 +1884,9 @@ msgstr "Você fez o push para"
msgid "LastPushEvent|at"
msgstr "em"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Saiba mais em"
@@ -1490,31 +1903,44 @@ msgid "Leave project"
msgstr "Sair do projeto"
msgid "License"
-msgstr ""
+msgstr "Licença"
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Limitado a mostrar %d evento, no máximo"
-msgstr[1] "Limitado a mostrar %d eventos, no máximo"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr "Bloquear"
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "Bloqueado"
msgid "Locked Files"
-msgstr ""
+msgstr "Arquivos bloqueados"
msgid "Login"
msgstr "Entrar"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr "Mar"
msgid "March"
msgstr "Março"
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "Máximo de falhas do git storage"
@@ -1527,6 +1953,9 @@ msgstr "Mediana"
msgid "Members"
msgstr "Membros"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr "Merge Requests"
@@ -1536,9 +1965,30 @@ msgstr "Eventos de merge"
msgid "Merge request"
msgstr "Merge requests"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "Mensagens"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "adicione uma chave SSH"
@@ -1548,17 +1998,29 @@ msgstr "Monitoramento"
msgid "More information is available|here"
msgstr "Mais informações estão disponíveis|aqui"
-msgid "Multiple issue boards"
+msgid "Move"
msgstr ""
-msgid "New Cluster"
-msgstr "Novo cluster"
+msgid "Move issue"
+msgstr ""
+
+msgid "Multiple issue boards"
+msgstr "Múltiplos issue boards"
+
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "Nova Issue"
msgstr[1] "Novas Issues"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Novo Agendamento de Pipeline"
@@ -1572,7 +2034,7 @@ msgid "New directory"
msgstr "Novo diretório"
msgid "New epic"
-msgstr ""
+msgstr "Novo épico"
msgid "New file"
msgstr "Novo arquivo"
@@ -1583,6 +2045,9 @@ msgstr "Novo grupo"
msgid "New issue"
msgstr "Nova issue"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Novo merge request"
@@ -1601,8 +2066,23 @@ msgstr "Novo subgrupo"
msgid "New tag"
msgstr "Nova tag"
-msgid "No container images stored for this project. Add one by following the instructions above."
-msgstr "Nenhuma imagem gravada para esse projeto. Adiciona uma com as instruções a seguir."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
msgid "No repository"
msgstr "Nenhum repositório"
@@ -1616,9 +2096,15 @@ msgstr "Nenhum tempo gasto"
msgid "None"
msgstr "Nenhum"
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "Não disponível"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "Dados insuficientes"
@@ -1679,6 +2165,12 @@ msgstr "Observar"
msgid "Notifications"
msgstr "Notificações"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr "Nov"
@@ -1688,8 +2180,8 @@ msgstr "Novembro"
msgid "Number of access attempts"
msgstr "Número de tentativas de acesso"
-msgid "Number of failures before backing off"
-msgstr "Número de falhas antes de reverter"
+msgid "OK"
+msgstr ""
msgid "Oct"
msgstr "Out"
@@ -1703,9 +2195,12 @@ msgstr "Filtrar"
msgid "Only project members can comment."
msgstr "Somente membros do projeto podem comentar."
-msgid "Opened"
+msgid "Open"
msgstr ""
+msgid "Opened"
+msgstr "Aberto"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Aberto"
@@ -1736,9 +2231,6 @@ msgstr "<< Primeiro"
msgid "Password"
msgstr "Senha"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "Pessoas sem permissão nunca receberão uma notificação e não serão capazes de comentar."
-
msgid "Pipeline"
msgstr "Pipeline"
@@ -1752,7 +2244,7 @@ msgid "Pipeline Schedules"
msgstr "Agendamentos da Pipeline"
msgid "Pipeline quota"
-msgstr ""
+msgstr "Cota de pipeline"
msgid "PipelineCharts|Failed:"
msgstr "Falhou:"
@@ -1781,12 +2273,6 @@ msgstr "Todos"
msgid "PipelineSchedules|Inactive"
msgstr "Inativo"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Chave da variável de entrada"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Valor da variável de entrada"
-
msgid "PipelineSchedules|Next Run"
msgstr "Próxima Execução"
@@ -1796,9 +2282,6 @@ msgstr "Nenhum"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "Digite uma descrição curta para esta pipeline"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Remova a linha da variável"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Tornar-se proprietário"
@@ -1826,6 +2309,12 @@ msgstr "Pipelines para a última semana"
msgid "Pipelines for last year"
msgstr "Pipelines para o último ano"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "todos"
@@ -1838,12 +2327,21 @@ msgstr "com etapa"
msgid "Pipeline|with stages"
msgstr "com etapas"
-msgid "Please solve the reCAPTCHA"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
msgstr ""
+msgid "Please solve the reCAPTCHA"
+msgstr "Por favor, resolva o reCAPTCHA"
+
msgid "Preferences"
msgstr "Preferências"
+msgid "Primary"
+msgstr ""
+
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."
@@ -1889,6 +2387,9 @@ msgstr "Sua conta é atualmente proprietária dos seguintes grupos:"
msgid "Profiles|your account"
msgstr "sua conta"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "O projeto '%{project_name}' está sendo excluído."
@@ -1904,6 +2405,15 @@ msgstr "Projeto '%{project_name}' atualizado com sucesso."
msgid "Project access must be granted explicitly to each user."
msgstr "Acesso ao projeto deve ser concedido explicitamente para cada usuário."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "Detalhes do projeto"
@@ -1922,6 +2432,21 @@ msgstr "Exportação do projeto iniciada. Um link para baixá-la será enviado p
msgid "ProjectActivityRSS|Subscribe"
msgstr "Inscreva-se"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Desabilitado"
@@ -1944,28 +2469,22 @@ msgid "ProjectNetworkGraph|Graph"
msgstr "Ãrvore"
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr ""
-
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr "Rodar pipeline na branch default imediatamente"
+msgstr "Fale com um administrador para mudar essa configuração."
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr ""
-
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr "Problema ao definir configurações de CI/CD Javascript"
+msgstr "Esse repositório só aceita push de commits assinados."
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr ""
+msgstr "Essa configuração é aplicada em nível de servidor e pode ser sobrescrita por qualquer administrador."
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr ""
+msgstr "Essa configuração está aplicada à nivel de servidor mas foi sobrescrita para esse projeto."
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr ""
+msgstr "Essa configuração será aplicada à todos os projetos, a não ser que seja sobrescrita pelo administrador."
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr ""
+msgstr "Usuários só podem fazer push de commits para esse repositório se os commits estiverem assinados com um de seus próprios e-mails verificados."
msgid "Projects"
msgstr "Projetos"
@@ -2019,11 +2538,14 @@ msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example
msgstr "URL da API base do Prometheus. como http://prometheus.example.com/"
msgid "PrometheusService|Time-series monitoring service"
-msgstr "Monitoramento com Prometheus"
+msgstr ""
msgid "PrometheusService|View environments"
msgstr "Ver ambientes"
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Público - O grupo e seus projetos podem ser visualizados por todos sem autenticação."
@@ -2031,12 +2553,15 @@ msgid "Public - The project can be accessed without any authentication."
msgstr "Público - O projeto pode ser acessado sem nenhuma autenticação."
msgid "Push Rules"
-msgstr ""
+msgstr "Regras de push"
msgid "Push events"
msgstr "Eventos de push"
msgid "PushRule|Committer restriction"
+msgstr "Restrição de commit"
+
+msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
@@ -2051,6 +2576,12 @@ msgstr "Branches"
msgid "RefSwitcher|Tags"
msgstr "Tags"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr "Registro"
@@ -2075,9 +2606,18 @@ msgstr "Merge Requests Relacionados"
msgid "Remind later"
msgstr "Lembrar mais tarde"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Remover projeto"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "Repositório"
@@ -2093,6 +2633,11 @@ msgstr "Recriar o token de status de saúde"
msgid "Reset runners registration token"
msgstr "Recriar o token de registro de runners"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Revert this commit"
msgstr "Reverter este commit"
@@ -2102,15 +2647,15 @@ msgstr "Reverter esse merge request"
msgid "SSH Keys"
msgstr "Chaves SSH"
-msgid "Save"
-msgstr "Salvar"
-
msgid "Save changes"
msgstr "Salvar alterações"
msgid "Save pipeline schedule"
msgstr "Salvar agendamento da pipeline"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "Agendar nova pipeline"
@@ -2121,43 +2666,64 @@ msgid "Scheduling Pipelines"
msgstr "Agendando pipelines"
msgid "Scoped issue boards"
-msgstr ""
+msgstr "Issue board de escopo"
msgid "Search branches and tags"
msgstr "Procurar branch e tags"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "Segundos antes de redefinir as informações de falha"
-msgid "Seconds to wait after a storage failure"
-msgstr "Segundos a esperar após uma falha de armazenamento"
-
msgid "Seconds to wait for a storage access attempt"
msgstr "Segundo de espera para tentativa de acesso ao storage"
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Selecionar Formato do Arquivo"
msgid "Select a timezone"
msgstr "Selecionar fuso horário"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Selecionar branch de destino"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr "Set"
msgid "September"
msgstr "Setembro"
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr "Modelos de serviço"
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Defina uma senha para sua conta para aceitar ou entregar código via %{protocol}."
-msgid "Set up CI"
-msgstr "Configurar CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "Configurar Koding"
@@ -2171,6 +2737,15 @@ msgstr "defina uma senha"
msgid "Settings"
msgstr "Configurações"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr "Mostrar páginas acima"
@@ -2183,35 +2758,44 @@ msgstr[0] "Mostrando %d evento"
msgstr[1] "Mostrando %d eventos"
msgid "Sidebar|Change weight"
-msgstr ""
-
-msgid "Sidebar|Edit"
-msgstr ""
+msgstr "Mudar peso"
msgid "Sidebar|No"
-msgstr ""
+msgstr "Não"
msgid "Sidebar|None"
-msgstr ""
+msgstr "Nenhum"
msgid "Sidebar|Weight"
-msgstr ""
+msgstr "Peso"
msgid "Snippets"
msgstr "Snippets"
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr "Algo deu errado do nosso lado."
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr "Algo deu errado ao tentar mudar o estado de ${this.issuableDisplayName}"
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr "Algo deu errado ao recuperar os projetos."
msgid "Something went wrong while fetching the registry list."
msgstr "Algo deu errado ao recuperar a lista de registro."
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "Ordenar por"
@@ -2255,7 +2839,7 @@ msgid "SortOptions|Least popular"
msgstr "Menos populares"
msgid "SortOptions|Less weight"
-msgstr ""
+msgstr "Menos peso"
msgid "SortOptions|Milestone"
msgstr "Milestone"
@@ -2267,7 +2851,7 @@ msgid "SortOptions|Milestone due soon"
msgstr "Milestone de fim mais próximo"
msgid "SortOptions|More weight"
-msgstr ""
+msgstr "Mais peso"
msgid "SortOptions|Most popular"
msgstr "Mais populares"
@@ -2309,7 +2893,7 @@ msgid "SortOptions|Start soon"
msgstr "Iniciar mais próximo"
msgid "SortOptions|Weight"
-msgstr ""
+msgstr "Peso"
msgid "Source"
msgstr "Origem"
@@ -2341,12 +2925,12 @@ msgstr "Inicie o Runner!"
msgid "Stopped"
msgstr "Parado"
+msgid "Storage"
+msgstr ""
+
msgid "Subgroups"
msgstr "Subgrupos"
-msgid "Subscribe"
-msgstr "Assine"
-
msgid "Switch branch/tag"
msgstr "Trocar branch/tag"
@@ -2437,13 +3021,16 @@ msgid "Team"
msgstr "Equipe"
msgid "Thanks! Don't show me this again"
-msgstr ""
+msgstr "Obrigado! Não mostrar novamente"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr "A pesquisa global avançada no GitLab é um serviço de pesquisa poderoso que economiza seu tempo. Ao invés de criar códigos duplicados e perder seu tempo, você pode agora pesquisar códigos de outros times que podem ajudar em seu projeto."
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
-msgstr "O limite do recuso do circuitbreaker deve ser inferior ao limite de contagem de falhas"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "A etapa de codificação mostra o tempo desde a entrega do primeiro commit até a criação do merge request. Os dados serão automaticamente adicionados aqui desde o momento de criação do merge request."
@@ -2457,21 +3044,18 @@ msgstr "O relacionamento como fork foi removido."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "A etapa de planejamento mostra o tempo que se leva desde a criação de uma issue até sua atribuição à um milestone, ou sua adição a uma lista no seu Issue Board. Comece a criar issues para ver dados para esta etapa."
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "The number of attempts GitLab will make to access a storage."
msgstr "O número de tentativas que gitlab fará para acessar um storage."
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
-msgstr "O número de falhas até o GitLab começar a desabilitar temporariamente o acesso a um nó de storage em um host"
-
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "O número de falhas para que o GitLab desabilite o acesso ao storage. O número de falhas pode ser redefinido na interface do administrador: %{link_to_health_page} ou %{api_documentation_link}."
msgid "The phase of the development lifecycle."
msgstr "A fase do ciclo de vida do desenvolvimento."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "O agendamento de pipeline executa pipelines no futuro, repetidamente, para branches ou tags específicas. Essas pipelines agendadas terão acesso limitado ao projeto baseado no seu usuário associado."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "A etapa de planejamento mostra o tempo do passo anterior até a publicação de seu primeiro conjunto de mudanças. Este tempo será adicionado automaticamente assim que você enviar seu primeiro conjunto de mudanças."
@@ -2502,20 +3086,47 @@ msgstr "Tempo em segundos para o GitLab manter as informações de falha. Se nen
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "Tempo em segundos que o GitLab tentará acessar o storage. Depois desse tempo, um erro de tempo excedido será disparado."
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "O tempo necessário por cada entrada de dados reunida por essa etapa."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "O valor situado no ponto médio de uma série de valores observados. Ex., entre 3, 5, 9, a mediana é 5. Entre 3, 5, 7, 8, a mediana é (5+7)/2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Há problemas para acessar o storage Git: "
-msgid "This board\\'s scope is reduced"
+msgid "There was an error loading users activity calendar."
msgstr ""
-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 "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "This board\\'s scope is reduced"
+msgstr "O escopo desse board está reduzido"
+
+msgid "This directory"
+msgstr ""
msgid "This is a confidential issue."
msgstr "Essa issue é confidencial."
@@ -2523,21 +3134,48 @@ msgstr "Essa issue é confidencial."
msgid "This is the author's first Merge Request to this project."
msgstr "Esse é o autor do primeiro merge request desse projeto."
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr "Essa issue é confidencial e está bloqueada."
msgid "This issue is locked."
msgstr "Essa issue está bloqueada."
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Isto significa que você não pode entregar código até que crie um repositório vazio ou importe um existente."
msgid "This merge request is locked."
msgstr "Esse merge request está bloqueado."
-msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgid "This project"
msgstr ""
+msgid "This repository"
+msgstr ""
+
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr "Esses e-mails se tornarão issues automaticamente (com os comentários se tornando uma conversa de e-mail) listadas aqui."
+
msgid "Time before an issue gets scheduled"
msgstr "Tempo até que uma issue seja agendada"
@@ -2547,9 +3185,21 @@ msgstr "Tempo até que uma issue comece a ser implementado"
msgid "Time between merge request creation and merge/close"
msgstr "Tempo entre a criação da solicitação de incorporação e a aceitação/fechamento"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Tempo até a primeira solicitação de incorporação"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "há %s dias"
@@ -2689,6 +3339,18 @@ msgstr "s"
msgid "Title"
msgstr "Título"
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "Tempo Total"
@@ -2704,20 +3366,41 @@ msgstr "Acompanhe a atividade com o Contribution Analytics."
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr "Acompanhe grupos de questões que compartilhem um tema, em projetos e milestones"
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr "Ativar Service Desk"
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr "Desbloquear"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Desbloqueado"
msgid "Unstar"
msgstr "Desmarcar"
-msgid "Unsubscribe"
-msgstr "Desassinar"
+msgid "Up to date"
+msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Atualize seu plano para ativar a Pesquisa Global Avançada."
@@ -2740,6 +3423,9 @@ msgstr "Enviar Novo Arquivo"
msgid "Upload file"
msgstr "Enviar arquivo"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "clique para fazer upload"
@@ -2752,9 +3438,15 @@ msgstr "Use o seguinte token de registro durante a configuração:"
msgid "Use your global notification setting"
msgstr "Utilizar configuração de notificação global"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr "Ver arquivo @ "
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "Ver merge request aberto"
@@ -2776,6 +3468,9 @@ msgstr "Desconhecido"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Precisa visualizar os dados? Solicite acesso ao administrador."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "Esta etapa não possui dados suficientes para exibição."
@@ -2788,9 +3483,6 @@ msgstr "Webhooks permitem que você acione uma URL se, por exemplo, quando um no
msgid "Weight"
msgstr "Peso"
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr "Falha ao acessar o storage. Gitlab impedirá o acesso ao storage pelo tempo especificado aqui. Isso permite que o sistema de arquivos se recupere. Repositórios que estiverem em nós com falha ficarão temporariamente indisponíveis"
-
msgid "Wiki"
msgstr "Wiki"
@@ -2809,6 +3501,12 @@ msgstr "É recomendado instalar %{markdown} para que as funções GFM sejam rend
msgid "WikiClone|Start Gollum and edit locally"
msgstr "Inicie o Gollum e edite localmente"
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr "Você não tem permissão para criar páginas web"
@@ -2911,9 +3609,21 @@ msgstr "Você está prestes a remover a relação de fork do projeto original %{
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Você irá transferir %{project_name_with_namespace} para outro proprietário. Tem certeza ABSOLUTA?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Você somente pode adicionar arquivos quando estiver em um branch"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "Você não pode escrever numa instância secundária de somente leitura do GitLab Geo. Por favor use %{link_to_primary_node}."
@@ -2953,6 +3663,12 @@ msgstr "Você não conseguirá fazer pull ou push no projeto via SSH até que a
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr "Você não poderá fazer push ou pull do código via SSH enquanto não adicionar sua chave SSH no seu perfil"
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr "Seu comentário não estará visível ao público."
@@ -2965,26 +3681,220 @@ msgstr "Seu nome"
msgid "Your projects"
msgstr "Seus projetos"
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr "nome da branch"
msgid "by"
msgstr "por"
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr "commit"
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "dia"
msgstr[1] "dias"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "novo merge request"
msgid "notification emails"
msgstr "emails de notificação"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "pai"
@@ -2996,12 +3906,21 @@ msgstr "senha"
msgid "personal access token"
msgstr "token de acesso pessoal"
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr "origem"
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "para ajudar seus contribuintes à se comunicar de maneira eficaz!"
msgid "username"
msgstr "nome do usuário"
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index 1b3b65325ac..0adb8e5b716 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:40-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 04:01-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Russian\n"
"Language: ru_RU\n"
@@ -16,26 +16,47 @@ msgstr ""
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d коммит"
msgstr[1] "%d коммита"
msgstr[2] "%d коммитов"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d Ñлой"
msgstr[1] "%d ÑлоÑ"
msgstr[2] "%d Ñлоёв"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s добавленный коммит был иÑключен Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñтью."
msgstr[1] "%s добавленных коммита были иÑключены Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñтью."
msgstr[2] "%s добавленных коммитов были иÑключены Ð´Ð»Ñ Ð¿Ñ€ÐµÐ´Ð¾Ñ‚Ð²Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð²Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð¾Ñтью."
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} добавил коммит %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -49,9 +70,6 @@ msgstr "на %{number_commits_behind} коммитов позади %{default_br
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab будет доÑтупен поÑле Ñледующей попытки."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab заблокирует доÑтуп на %{number_of_seconds} Ñекунд."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} из %{maximum_failures} возможных неудачных попыток. GitLab не будет автоматичеÑки повторÑÑ‚ÑŒ попытку. СброÑьте информацию хранилища поÑле уÑÑ‚Ñ€Ð°Ð½ÐµÐ½Ð¸Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñ‹."
@@ -62,7 +80,7 @@ msgstr[1] "%{storage_name}: %{failed_attempts} - неудачные попытк
msgstr[2] "%{storage_name}: %{failed_attempts} - неудачные попытки доÑтупа к хранилищу:"
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} доÑтупен"
msgid "(checkout the %{link} for information on how to install it)."
msgstr "(перейдите по ÑÑылке %{link} Ð´Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ð¸ об уÑтановке)."
@@ -110,7 +128,7 @@ msgid "Activity"
msgstr "ÐктивноÑÑ‚ÑŒ"
msgid "Add"
-msgstr ""
+msgstr "Добавить"
msgid "Add Changelog"
msgstr "Добавить Журнал Изменений"
@@ -119,7 +137,7 @@ msgid "Add Contribution guide"
msgstr "Добавить РуководÑтво учаÑтника"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Добавить групповые веб-обработчики и GitLab Enterprise Edition."
msgid "Add License"
msgstr "Добавить Лицензию"
@@ -127,22 +145,79 @@ msgstr "Добавить Лицензию"
msgid "Add new directory"
msgstr "Добавить новый каталог"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "Ñтраница работоÑпоÑобноÑти"
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr "РаÑширенные наÑтройки"
msgid "All"
msgstr "Ð’Ñе"
-msgid "An error occurred when toggling the notification subscription"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
msgstr ""
+msgid "An error occurred when toggling the notification subscription"
+msgstr "Произошла ошибка при переключении подпиÑки на оповещениÑ"
+
msgid "An error occurred when updating the issue weight"
+msgstr "Произошла ошибка при обновлении веÑа обÑуждениÑ"
+
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
msgstr ""
msgid "An error occurred while fetching sidebar data"
+msgstr "Произошла ошибка при получении денег данных Ð´Ð»Ñ Ð±Ð¾ÐºÐ¾Ð²Ð¾Ð¹ панели"
+
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
msgstr ""
msgid "An error occurred. Please try again."
@@ -169,9 +244,6 @@ msgstr "Ð’Ñ‹ дейÑтвительно хотите удалить Ñто раÑ
msgid "Are you sure you want to discard your changes?"
msgstr "Ð’Ñ‹ уверены, что хотите отменить ваши изменениÑ?"
-msgid "Are you sure you want to leave this group?"
-msgstr "Ð’Ñ‹ уверены, что хотите покинуть Ñту группу?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "Ð’Ñ‹ уверены, что хотите ÑброÑить Ñтот региÑтрационный токен?"
@@ -184,6 +256,21 @@ msgstr "Вы уверены?"
msgid "Artifacts"
msgstr "Ðртефакты"
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Приложить файл через drag &amp; drop или %{upload_link}"
@@ -199,15 +286,18 @@ msgstr "Журнал аутентификации"
msgid "Author"
msgstr "Ðвтор"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого ревью и автоматичеÑкого Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ домена и %{kubernetes} Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы."
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого ревью и автоматичеÑкого Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ Ð¸Ð¼ÐµÐ½Ð¸ домена Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы."
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "ÐŸÑ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑкого ревью и автоматичеÑкого Ñ€Ð°Ð·Ð²Ñ‘Ñ€Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ñ‚Ñ€ÐµÐ±ÑƒÑŽÑ‚ ÑƒÐºÐ°Ð·Ð°Ð½Ð¸Ñ %{kubernetes} Ð´Ð»Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð¹ работы."
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "Auto DevOps (бета)"
@@ -227,11 +317,17 @@ msgid "AutoDevOps|You can activate %{link_to_settings} for this project."
msgstr "Ð’Ñ‹ можете активировать %{link_to_settings} Ð´Ð»Ñ Ñтого проекта."
msgid "Available"
+msgstr "ДоÑтупен"
+
+msgid "Avatar will be removed. Are you sure?"
msgstr ""
-msgid "Billing"
+msgid "Average per day: %{average}"
msgstr ""
+msgid "Billing"
+msgstr "Тариф"
+
msgid "BillingPlans|%{group_name} is currently on the %{plan_link} plan."
msgstr "%{group_name} иÑпользует тарифный план %{plan_link}."
@@ -283,6 +379,9 @@ msgstr "оплачиваетÑÑ ÐµÐ¶ÐµÐ³Ð¾Ð´Ð½Ð¾ в размере %{price_per_
msgid "BillingPlans|per user"
msgstr "за пользователÑ"
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "Ветка"
@@ -296,10 +395,10 @@ msgid "Branch has changed"
msgstr "Ветка была изменена"
msgid "Branch is already taken"
-msgstr ""
+msgstr "Ветвь уже ÑущеÑтвует"
msgid "Branch name"
-msgstr ""
+msgstr "Ð˜Ð¼Ñ Ð²ÐµÑ‚Ð²Ð¸"
msgid "BranchSwitcherPlaceholder|Search branches"
msgstr "ПоиÑк веток"
@@ -362,7 +461,7 @@ msgid "Branches|Sort by"
msgstr "Сортировать по"
msgid "Branches|The branch could not be updated automatically because it has diverged from its upstream counterpart."
-msgstr ""
+msgstr "Ветвь не может быть обновлена автоматичеÑки, потому что она имеет раÑÑ…Ð¾Ð¶Ð´ÐµÐ½Ð¸Ñ Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑким репозиторием."
msgid "Branches|The default branch cannot be deleted"
msgstr "Ветка \"по умолчанию\" не может быть удалена"
@@ -377,13 +476,13 @@ msgid "Branches|To confirm, type %{branch_name_confirmation}:"
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð´Ñ‚Ð²ÐµÑ€Ð¶Ð´ÐµÐ½Ð¸Ñ, введите %{branch_name_confirmation}:"
msgid "Branches|To discard the local changes and overwrite the branch with the upstream version, delete it here and choose 'Update Now' above."
-msgstr ""
+msgstr "Чтобы отменить локальные Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð¸ перезапиÑать ветвь верÑией из родительÑкого репозиториÑ, удалите её здеÑÑŒ и выберите \"Обновить ÑейчаÑ\" выше."
msgid "Branches|You’re about to permanently delete the protected branch %{branch_name}."
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ безвозвратно удалить защищённую ветку %{branch_name}."
msgid "Branches|diverged from upstream"
-msgstr ""
+msgstr "раÑходитÑÑ Ñ Ñ€Ð¾Ð´Ð¸Ñ‚ÐµÐ»ÑŒÑким репозиторием"
msgid "Branches|merged"
msgstr "влита"
@@ -412,8 +511,8 @@ msgstr "по автору"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "ÐаÑтройка CI"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr "ЗаданиÑ"
@@ -424,9 +523,12 @@ msgstr "Отмена"
msgid "Cancel edit"
msgstr "Отменить редактирование"
-msgid "Change Weight"
+msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Change Weight"
+msgstr "Изменить ВеÑ"
+
msgid "ChangeTypeActionLabel|Pick into branch"
msgstr "Выбрать в ветке"
@@ -439,20 +541,29 @@ msgstr "Подобрать"
msgid "ChangeTypeAction|Revert"
msgstr "Отменить"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "Журнал изменений"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "Диаграммы"
msgid "Chat"
msgstr "Чат"
-msgid "Checking %{text} availability…"
+msgid "Check interval"
msgstr ""
+msgid "Checking %{text} availability…"
+msgstr "Проверка доÑтупноÑти %{text} ..."
+
msgid "Checking branch availability..."
-msgstr ""
+msgstr "Проверка доÑтупноÑти ветви..."
msgid "Cherry-pick this commit"
msgstr "Подобрать в Ñтом коммите"
@@ -460,7 +571,19 @@ msgstr "Подобрать в Ñтом коммите"
msgid "Cherry-pick this merge request"
msgstr "Подобрать Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -517,175 +640,220 @@ msgstr "пропущено"
msgid "CiStatus|running"
msgstr "выполнÑетÑÑ"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
-msgstr "CircuitBreaker API"
-
-msgid "Clone repository"
-msgstr "Клонировать репозиторий"
+msgid "CiVariables|Input variable key"
+msgstr ""
-msgid "Close"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Cluster"
-msgstr "КлаÑтер"
+msgid "CiVariables|Remove variable row"
+msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr "CircuitBreaker API"
+
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Clone repository"
+msgstr "Клонировать репозиторий"
+
+msgid "Close"
+msgstr "Закрыть"
+
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
-msgstr "Параметры клаÑтера"
+msgid "ClusterIntegration|API URL"
+msgstr "ÐÐ´Ñ€ÐµÑ API"
-msgid "ClusterIntegration|Cluster integration"
-msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтеров"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтеров отключена Ð´Ð»Ñ Ñтого проекта."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтеров включена Ð´Ð»Ñ Ñтого проекта."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "Ð”Ð»Ñ Ñтого проекта включена Ð¸Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ ÐºÐ»Ð°Ñтеров. Отключение интеграции не повлиÑет на клаÑтер, но Ñоединение Ñ GitLab будет временно отключено."
+msgid "ClusterIntegration|Applications"
+msgstr "ПриложениÑ"
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Cluster name"
-msgstr "Ðазвание клаÑтера"
+msgid "ClusterIntegration|CA Certificate"
+msgstr "Сертификат удоÑтоверÑющего центра"
+
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgstr "Комплект Ñертификатов удоÑтоверÑющего центра (формат PEM)"
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Copy API URL"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
+msgid "ClusterIntegration|Copy API URL"
+msgstr "Скопировать Ð°Ð´Ñ€ÐµÑ API"
+
msgid "ClusterIntegration|Copy CA Certificate"
-msgstr ""
+msgstr "Копировать Сертификат УдоÑтоверÑющего Центра"
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "Копировать название клаÑтера"
+msgid "ClusterIntegration|Copy Token"
+msgstr "Скопировать Токен"
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
-msgstr "Создать клаÑтер"
-
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create on GKE"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "Включить интеграцию Ñ ÐºÐ»Ð°Ñтерами"
+msgid "ClusterIntegration|Create on GKE"
+msgstr "Создать в GKE"
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
-msgstr ""
+msgstr "Укажите параметры ÑущеÑтвующего клаÑтера Kubernetes"
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
-msgstr ""
+msgstr "GitLab Runner"
msgid "ClusterIntegration|Google Cloud Platform project ID"
msgstr "Идентификатор проекта в Google Cloud Platform"
msgid "ClusterIntegration|Google Kubernetes Engine"
-msgstr ""
+msgstr "Google Kubernetes Engine"
msgid "ClusterIntegration|Google Kubernetes Engine project"
-msgstr ""
+msgstr "Проект Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
+msgstr "Helm Tiller"
+
+msgid "ClusterIntegration|Ingress"
+msgstr "Ingress"
+
+msgid "ClusterIntegration|Install"
+msgstr "УÑтановить"
+
+msgid "ClusterIntegration|Installed"
+msgstr "УÑтановлен"
+
+msgid "ClusterIntegration|Installing"
+msgstr "УÑтановка"
+
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
msgstr ""
-msgid "ClusterIntegration|Inactive"
+msgid "ClusterIntegration|Integration status"
msgstr ""
-msgid "ClusterIntegration|Ingress"
+msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Install"
+msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
+msgid "ClusterIntegration|Kubernetes cluster integration"
msgstr ""
-msgid "ClusterIntegration|Installed"
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
msgstr ""
-msgid "ClusterIntegration|Installing"
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "Узнайте больше на %{link_to_documentation}"
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr "Тип машины"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "УбедитеÑÑŒ, что ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ %{link_to_requirements} Ð´Ð»Ñ ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ ÐºÐ»Ð°Ñтеров"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "УправлÑйте клаÑтером, Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ Ð¿Ð¾ ÑÑылке %{link_gke}"
+msgid "ClusterIntegration|More information"
+msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
msgid "ClusterIntegration|Note:"
@@ -694,47 +862,44 @@ msgstr "Примечание:"
msgid "ClusterIntegration|Number of nodes"
msgstr "КоличеÑтво узлов"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "ПожалуйÑта, убедитеÑÑŒ, что ваш аккаунт Google отвечает Ñледующим требованиÑм:"
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr ""
-
msgid "ClusterIntegration|Project ID"
-msgstr ""
+msgstr "ID проекта"
msgid "ClusterIntegration|Project namespace"
-msgstr ""
+msgstr "ПроÑтранÑтво имён проекта"
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "ПроÑтранÑтво имен проекта (необÑзательное, уникальное)"
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
-msgstr "Прочтите нашу документацию %{link_to_help_page} по интеграции клаÑтера."
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
-msgstr "Удалить интеграцию Ñ ÐºÐ»Ð°Ñтером"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr "Удалить интеграцию"
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
-msgstr ""
+msgstr "Ðе удалоÑÑŒ выполнить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° запуÑк процеÑÑа уÑтановки"
msgid "ClusterIntegration|Save changes"
-msgstr ""
+msgstr "Сохранить изменениÑ"
-msgid "ClusterIntegration|See and edit the details for your cluster"
-msgstr "ПроÑмотреть и отредактировать параметры Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ³Ð¾ клаÑтера"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|See machine types"
msgstr "См. типы машин"
@@ -746,55 +911,55 @@ msgid "ClusterIntegration|See zones"
msgstr "См. зоны"
msgid "ClusterIntegration|Service token"
-msgstr ""
+msgstr "Служебный токен"
msgid "ClusterIntegration|Show"
-msgstr ""
+msgstr "Показать"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr " У Ð½Ð°Ñ Ñ‡Ñ‚Ð¾-то пошло не так."
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
-msgstr ""
+msgstr "Произошли ошибки во Ð²Ñ€ÐµÐ¼Ñ ÑƒÑтановки %{title}"
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
-msgstr "Переключить КлаÑтер"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|Token"
-msgstr ""
+msgstr "Токен"
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "ЕÑли привÑзать клаÑтер к Ñтому проекту, вы Ñ Ð»Ñ‘Ð³ÐºÐ¾Ñтью Ñможете иÑпользовать Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñ€ÐµÐ²ÑŒÑŽ, развертывать ваши приложениÑ, запуÑкать Ñборочные линии и многое другое."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
-msgstr ""
+msgstr "Ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ должна иметь %{link_to_kubernetes_engine}"
msgid "ClusterIntegration|Zone"
msgstr "Зона"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
-msgstr ""
+msgstr "доÑтуп к Google Kubernetes Engine"
-msgid "ClusterIntegration|cluster"
-msgstr "клаÑтер"
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
msgid "ClusterIntegration|documentation"
-msgstr ""
+msgstr "документациÑ"
msgid "ClusterIntegration|help page"
msgstr "Ñтраница Ñправки"
msgid "ClusterIntegration|installing applications"
-msgstr ""
+msgstr "ClusterIntegration | уÑтановка приложений"
msgid "ClusterIntegration|meets the requirements"
msgstr "отвечает требованиÑм"
@@ -802,6 +967,9 @@ msgstr "отвечает требованиÑм"
msgid "ClusterIntegration|properly configured"
msgstr "правильно наÑтроен"
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "Комментарии"
@@ -820,6 +988,9 @@ msgstr "ПродолжительноÑÑ‚ÑŒ поÑледних 30 коммитоÐ
msgid "Commit message"
msgstr "ОпиÑание коммита"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Коммит"
@@ -832,15 +1003,57 @@ msgstr "Коммиты"
msgid "Commits feed"
msgstr "Лента коммитов"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "ИÑториÑ"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "ЗафикÑировано автором"
msgid "Compare"
msgstr "Сравнить"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr "РееÑÑ‚Ñ€ Контейнеров"
@@ -892,30 +1105,42 @@ msgstr "РуководÑтво учаÑтника"
msgid "Contributors"
msgstr "УчаÑтники"
-msgid "ContributorsPage|Building repository graph."
+msgid "ContributorsPage|%{startDate} – %{endDate}"
msgstr ""
+msgid "ContributorsPage|Building repository graph."
+msgstr "ПоÑтроение графа репозиториÑ."
+
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
-msgstr ""
+msgstr "Коммиты в %{branch_name}, за иÑключением коммитов ÑлиÑниÑ. Ограничено 6,000 коммитами."
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
-msgstr ""
+msgstr "ПожалуйÑта подождите, Ñта Ñтраница автоматичеÑки обновитÑÑ Ð¿Ð¾ готовноÑти."
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
-msgstr ""
+msgstr "Контролировать макÑимальное количеÑтво потоков фоновой загрузки LFS/вложений Ð´Ð»Ñ Ñтого вторичного узла"
msgid "Control the maximum concurrency of repository backfill for this secondary node"
-msgstr ""
+msgstr "Контролировать макÑимальное количеÑтво потоков фоновой загрузки хранилища Ð´Ð»Ñ Ñтого вторичного узла"
msgid "Copy SSH public key to clipboard"
-msgstr ""
+msgstr "Скопировать публичный ключ SSH в буфер обмена"
msgid "Copy URL to clipboard"
msgstr "Копировать URL в буфер обмена"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "Копировать SHA коммита в буфер обмена"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "Создать Ðовый каталог"
@@ -929,11 +1154,14 @@ msgid "Create empty bare repository"
msgstr "Создать пуÑтой репозиторий"
msgid "Create epic"
-msgstr ""
+msgstr "Создать Ñпик"
msgid "Create file"
msgstr "Создать файл"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Создать Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
@@ -946,6 +1174,9 @@ msgstr "Создать новый каталог"
msgid "Create new file"
msgstr "Создать новый файл"
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "Ðовый"
@@ -959,7 +1190,7 @@ msgid "CreateTokenToCloneLink|create a personal access token"
msgstr "Ñоздать перÑональный токен доÑтупа"
msgid "Creating epic"
-msgstr ""
+msgstr "Создание Ñпика"
msgid "Cron Timezone"
msgstr "Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð° Cron"
@@ -967,6 +1198,9 @@ msgstr "Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð° Cron"
msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Cron"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð½Ð°Ñтраиваемых уведомлений"
@@ -976,9 +1210,6 @@ msgstr "ÐаÑтраиваемые уровни уведомлений аналÐ
msgid "Cycle Analytics"
msgstr "Ðналитика Цикла"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "Ðналитика Цикла дает предÑтавление о том, Ñколько времени требуетÑÑ, чтобы перейти от идеи к производÑтву в вашем проекте."
-
msgid "CycleAnalyticsStage|Code"
msgstr "ÐапиÑание кода"
@@ -1031,22 +1262,31 @@ msgid "Description"
msgstr "ОпиÑание"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr ""
+msgstr "Шаблоны опиÑаний позволÑÑŽÑ‚ вам определить контекÑтно-завиÑимые шаблоны Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¾Ð±Ñуждений и запроÑов на ÑлиÑние в вашем проекте."
msgid "Details"
msgstr "ÐŸÐ¾Ð´Ñ€Ð¾Ð±Ð½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Ð˜Ð¼Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ð°"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "Отменить изменениÑ"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Отключить блок Ð²Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð² Ðналитику Цикла"
msgid "Dismiss Merge Request promotion"
-msgstr ""
+msgstr "Отключить анонÑÑ‹ Ð´Ð»Ñ Ð—Ð°Ð¿Ñ€Ð¾Ñов на ÑлиÑние"
msgid "Don't show again"
msgstr "Ðе показывать Ñнова"
@@ -1078,79 +1318,109 @@ msgstr "ПроÑтой Diff"
msgid "DownloadSource|Download"
msgstr "Скачать"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "Редактировать"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Изменить раÑпиÑание Ñборочной линии %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "Email-адреÑа"
-msgid "Environments|An error occurred while fetching the environments."
+msgid "Enable"
msgstr ""
+msgid "Environments|An error occurred while fetching the environments."
+msgstr "Произошла ошибка при получении окружений."
+
msgid "Environments|An error occurred while making the request."
-msgstr ""
+msgstr "Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа."
msgid "Environments|Commit"
-msgstr ""
+msgstr "Коммит"
msgid "Environments|Deployment"
-msgstr ""
+msgstr "Развертывание"
msgid "Environments|Environment"
-msgstr ""
+msgstr "Окружение"
msgid "Environments|Environments"
-msgstr ""
-
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
+msgstr "ОкружениÑ"
msgid "Environments|Job"
-msgstr ""
+msgstr "Задание"
msgid "Environments|New environment"
-msgstr ""
+msgstr "Ðовое окружение"
msgid "Environments|No deployments yet"
-msgstr ""
+msgstr "Еще нет развертываний"
msgid "Environments|Open"
-msgstr ""
+msgstr "Открыть"
msgid "Environments|Re-deploy"
-msgstr ""
+msgstr "Переразвернуть"
msgid "Environments|Read more about environments"
-msgstr ""
+msgstr "Подробнее об окружениÑÑ…"
msgid "Environments|Rollback"
-msgstr ""
+msgstr "Откатить"
msgid "Environments|Show all"
msgstr "Показать вÑе"
msgid "Environments|Updated"
-msgstr ""
+msgstr "Обновлено"
msgid "Environments|You don't have any environments right now."
-msgstr ""
+msgstr "Ð’Ñ‹ пока не наÑтроили ни одного окружениÑ."
msgid "Epic will be removed! Are you sure?"
-msgstr ""
+msgstr "Эпик будет удален! Вы уверены?"
msgid "Epics"
-msgstr ""
+msgstr "Эпики"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr ""
+msgstr "Эпики позволÑÑ‚ вам управлÑÑ‚ÑŒ портфелем проектов более Ñффективно и Ñ Ð¼ÐµÐ½ÑŒÑˆÐ¸Ð¼Ð¸ уÑилиÑми"
msgid "Error creating epic"
+msgstr "Ошибка ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ñпика"
+
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
msgstr ""
msgid "Error occurred when toggling the notification subscription"
+msgstr "Произошла ошибка при переключении подпиÑки на оповещение"
+
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
msgstr ""
msgid "EventFilterBy|Filter by all"
@@ -1180,6 +1450,9 @@ msgstr "ЕжемеÑÑчно (каждое 1-е чиÑло в 4:00)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Еженедельно (по воÑкреÑениÑм в 4:00)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr "Обзор проектов"
@@ -1198,6 +1471,9 @@ msgstr "Фев."
msgid "February"
msgstr "Февраль"
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "Ð˜Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
@@ -1243,42 +1519,138 @@ msgstr "От запроÑа на ÑлиÑние до развертываниÑ
msgid "GPG Keys"
msgstr "GPG Ключи"
+msgid "Generate a default set of labels"
+msgstr ""
+
msgid "Geo Nodes"
+msgstr "ГеографичеÑкие Узлы"
+
+msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgstr "Ðа узле Ñбой или он не работает."
+
+msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgstr "Узел функционирует медленно, перегужен или только что воÑÑтановлен поÑле ÑбоÑ."
+
+msgid "GeoNodes|Database replication lag:"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodeSyncStatus|Node is failing or broken."
+msgid "GeoNodes|Does not match the primary storage configuration"
msgstr ""
-msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
+msgid "GeoNodes|Failed"
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Full"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
+msgstr "Объем хранилища Ð´Ð»Ñ Ñинхронизации файлов"
+
+msgid "Geo|Groups to synchronize"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Projects in certain groups"
msgstr ""
-msgid "Geo|Repository sync capacity"
+msgid "Geo|Projects in certain storage shards"
msgstr ""
+msgid "Geo|Repository sync capacity"
+msgstr "Объем хранилища Ð´Ð»Ñ Ñинхронизации репозиториÑ"
+
msgid "Geo|Select groups to replicate."
+msgstr "Выберите группы Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ð¸ÐºÐ°Ñ†Ð¸Ð¸."
+
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
msgstr ""
msgid "Git storage health information has been reset"
msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¾ ÑтабильноÑти Git хранилища была Ñброшена"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "Ð¡ÐµÐºÑ†Ð¸Ñ Gitlab Runner"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Перейти к вашему ответвлению"
@@ -1288,6 +1660,9 @@ msgstr "Ответвление"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Google не %{link_to_documentation}. ПопроÑите Ñвоего админиÑтратора GitLab, еÑли вы хотите воÑпользоватьÑÑ Ñтим ÑервиÑом."
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Запретить публикацию проектов из %{group} в других группах"
@@ -1324,8 +1699,8 @@ msgstr "Группы не найдены"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Ð’Ñ‹ можете управлÑÑ‚ÑŒ правами и доÑтупом учаÑтников вашей группы к каждому проекту в группе."
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
-msgstr "Вы уверены, что вы хотите покинуть группу \"${this.group.fullName}\"?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
+msgstr ""
msgid "GroupsTree|Create a project in this group."
msgstr "Создать проект в Ñтой группе."
@@ -1355,7 +1730,7 @@ msgid "GroupsTree|Sorry, no groups or projects matched your search"
msgstr "К Ñожалению, по вашему запроÑу групп или проектов не найдено"
msgid "Have your users email"
-msgstr ""
+msgstr "Ð­Ð»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð°Ñ Ð¿Ð¾Ñ‡Ñ‚Ð° Ð´Ð»Ñ Ð¾Ð±Ñ€Ð°Ñ‰ÐµÐ½Ð¸Ð¹ пользователей"
msgid "Health Check"
msgstr "Проверка работоÑпоÑобноÑти"
@@ -1375,6 +1750,12 @@ msgstr "Проблем работоÑпоÑобноÑти не обнаружеÐ
msgid "HealthCheck|Unhealthy"
msgstr "ÐеÑтабильный"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "History"
msgstr "ИÑториÑ"
@@ -1385,22 +1766,28 @@ msgid "Import repository"
msgstr "Импорт репозиториÑ"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr ""
+msgstr "Улучшить доÑки обÑуждений Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ GitLab Enterprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Улучшить управление обÑуждениÑми возможноÑтью Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ Ð²ÐµÑа обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¸ GitLab Enterprise Edition."
msgid "Improve search with Advanced Global Search and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Улучшить поиÑк при помощи РаÑширенного Глобального ПоиÑка и GitLab Enterprise Edition."
msgid "Install a Runner compatible with GitLab CI"
msgstr "УÑтановите Gitlab Runner ÑовмеÑтимый Ñ Gitlab CI"
msgid "Instance"
msgid_plural "Instances"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "ЭкземплÑÑ€"
+msgstr[1] "ЭкземплÑра"
+msgstr[2] "ЭкземплÑров"
+
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Внутренний - Группу и включённые в неё проекты может видеть любой зарегиÑтрированный пользователь."
@@ -1415,7 +1802,7 @@ msgid "Introducing Cycle Analytics"
msgstr "Внедрение Цикла Ðналитик"
msgid "Issue board focus mode"
-msgstr ""
+msgstr "Режим фокуÑировки над доÑкой обÑуждений"
msgid "Issue events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ð±Ñуждений"
@@ -1424,11 +1811,14 @@ msgid "IssueBoards|Board"
msgstr "ДоÑка"
msgid "IssueBoards|Boards"
-msgstr ""
+msgstr "ДоÑки"
msgid "Issues"
msgstr "ОбÑуждениÑ"
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr "Янв."
@@ -1447,6 +1837,27 @@ msgstr "Июн."
msgid "June"
msgstr "Июнь"
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Отключено"
@@ -1456,6 +1867,9 @@ msgstr "Включено"
msgid "Labels"
msgstr "Метки"
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "ПоÑледний %d день"
@@ -1486,6 +1900,9 @@ msgstr "Вы отправили в"
msgid "LastPushEvent|at"
msgstr "в"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Узнайте больше в"
@@ -1502,32 +1919,44 @@ msgid "Leave project"
msgstr "Покинуть проект"
msgid "License"
-msgstr ""
+msgstr "ЛицензиÑ"
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "Показывать %d Ñобытие макÑимум"
-msgstr[1] "Показывать %d ÑÐ¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¼Ð°ÐºÑимум"
-msgstr[2] "Показывать %d Ñобытий макÑимум"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr "Блокировка"
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "Заблокировано"
msgid "Locked Files"
-msgstr ""
+msgstr "Заблокированные Файлы"
msgid "Login"
msgstr "Войти"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr "Мар."
msgid "March"
msgstr "Март"
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "МакÑимальное количеÑтво Ñбоев хранилища git"
@@ -1540,6 +1969,9 @@ msgstr "Среднее"
msgid "Members"
msgstr "УчаÑтники"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr "ЗапроÑÑ‹ на СлиÑние"
@@ -1549,9 +1981,30 @@ msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ ÑлиÑний"
msgid "Merge request"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "СообщениÑ"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "добавить ключ SSH"
@@ -1561,11 +2014,17 @@ msgstr "Мониторинг"
msgid "More information is available|here"
msgstr "Больше информации доÑтупно|тут"
-msgid "Multiple issue boards"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
msgstr ""
-msgid "New Cluster"
-msgstr "Ðовый КлаÑтер"
+msgid "Multiple issue boards"
+msgstr "Сводные доÑки обÑуждений"
+
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
@@ -1573,6 +2032,12 @@ msgstr[0] "Ðовое ОбÑуждение"
msgstr[1] "Ðовых ОбÑуждениÑ"
msgstr[2] "Ðовых ОбÑуждений"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Ðовое РаÑпиÑание Сборочной Линии"
@@ -1580,13 +2045,13 @@ msgid "New branch"
msgstr "ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°"
msgid "New branch unavailable"
-msgstr ""
+msgstr "ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ° недоÑтупна"
msgid "New directory"
msgstr "Ðовый каталог"
msgid "New epic"
-msgstr ""
+msgstr "Ðовый Ñпик"
msgid "New file"
msgstr "Ðовый файл"
@@ -1597,6 +2062,9 @@ msgstr "ÐÐ¾Ð²Ð°Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ð°"
msgid "New issue"
msgstr "Ðовое обÑуждение"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "Ðовый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
@@ -1615,8 +2083,23 @@ msgstr "ÐÐ¾Ð²Ð°Ñ Ð¿Ð¾Ð´Ð³Ñ€ÑƒÐ¿Ð¿Ð°"
msgid "New tag"
msgstr "Ðовый тег"
-msgid "No container images stored for this project. Add one by following the instructions above."
-msgstr "Ðет образов контейнеров Ð´Ð»Ñ Ñтого проекта. Добавьте образ, ÑÐ»ÐµÐ´ÑƒÑ Ð¸Ð½ÑтрукциÑм выше."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
msgid "No repository"
msgstr "Ðет репозиториÑ"
@@ -1625,14 +2108,20 @@ msgid "No schedules"
msgstr "Ðет раÑпиÑаний"
msgid "No time spent"
-msgstr ""
+msgstr "Ðет затраченного времени"
msgid "None"
msgstr "ПуÑто"
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "ÐедоÑтупно"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "ÐедоÑтаточно данных"
@@ -1693,6 +2182,12 @@ msgstr "ОтÑлеживать"
msgid "Notifications"
msgstr "УведомлениÑ"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr "ÐоÑб."
@@ -1702,8 +2197,8 @@ msgstr "ÐоÑбрь"
msgid "Number of access attempts"
msgstr "КоличеÑтво попыток доÑтупа"
-msgid "Number of failures before backing off"
-msgstr "КоличеÑтво ошибок перед откатом"
+msgid "OK"
+msgstr ""
msgid "Oct"
msgstr "Окт."
@@ -1717,9 +2212,12 @@ msgstr "Фильтр"
msgid "Only project members can comment."
msgstr "Только учаÑтники проекта могут оÑтавлÑÑ‚ÑŒ комментарии."
-msgid "Opened"
+msgid "Open"
msgstr ""
+msgid "Opened"
+msgstr "Открыт"
+
msgid "OpenedNDaysAgo|Opened"
msgstr "Открыто"
@@ -1750,9 +2248,6 @@ msgstr "« ПерваÑ"
msgid "Password"
msgstr "Пароль"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "Люди без разрешений не получат уведомление и не Ñмогут комментировать."
-
msgid "Pipeline"
msgstr "Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ð°Ñ Ð»Ð¸Ð½Ð¸Ñ"
@@ -1766,7 +2261,7 @@ msgid "Pipeline Schedules"
msgstr "РаÑпиÑÐ°Ð½Ð¸Ñ Ð¡Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ñ‹Ñ… Линий"
msgid "Pipeline quota"
-msgstr ""
+msgstr "Квота Ñборочной линии"
msgid "PipelineCharts|Failed:"
msgstr "Ðеудача:"
@@ -1795,12 +2290,6 @@ msgstr "Ð’Ñе"
msgid "PipelineSchedules|Inactive"
msgstr "Ðеактивно"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Ввод ключевой переменной"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "Ð’Ñтавить значение"
-
msgid "PipelineSchedules|Next Run"
msgstr "Следующий запуÑк"
@@ -1810,9 +2299,6 @@ msgstr "ОтÑутÑтвует"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "ПредоÑтавьте краткое опиÑание Ñтой Ñборочной линии"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Удалить значение"
-
msgid "PipelineSchedules|Take ownership"
msgstr "Стать владельцем"
@@ -1840,6 +2326,12 @@ msgstr "Сборочные линии за поÑледнюю неделю"
msgid "Pipelines for last year"
msgstr "Сборочные линии за поÑледний год"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "вÑе"
@@ -1852,12 +2344,21 @@ msgstr "Ñо Ñтадией"
msgid "Pipeline|with stages"
msgstr "Ñо ÑтадиÑми"
-msgid "Please solve the reCAPTCHA"
+msgid "Play"
msgstr ""
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
+msgid "Please solve the reCAPTCHA"
+msgstr "ПожалуйÑта, решите reCAPTCHA"
+
msgid "Preferences"
msgstr "ПредпочтениÑ"
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Приватный - ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю."
@@ -1903,6 +2404,9 @@ msgstr "Ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ в наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ñ
msgid "Profiles|your account"
msgstr "ваша ÑƒÑ‡ÐµÑ‚Ð½Ð°Ñ Ð·Ð°Ð¿Ð¸ÑÑŒ"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Проект '%{project_name}' находитÑÑ Ð² процеÑÑе удалениÑ."
@@ -1918,6 +2422,15 @@ msgstr "Проект '%{project_name}' уÑпешно обновлен."
msgid "Project access must be granted explicitly to each user."
msgstr "ДоÑтуп к проекту должен предоÑтавлÑÑ‚ÑŒÑÑ Ñвно каждому пользователю."
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "Детали проекта"
@@ -1936,6 +2449,21 @@ msgstr "Ðачат ÑкÑпорт проекта. СÑылка Ð´Ð»Ñ Ñкачи
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПодпиÑатьÑÑ"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "Отключено"
@@ -1958,28 +2486,22 @@ msgid "ProjectNetworkGraph|Graph"
msgstr "Граф"
msgid "ProjectSettings|Contact an admin to change this setting."
-msgstr ""
-
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
+msgstr "ОбратитеÑÑŒ к админиÑтратору Ð´Ð»Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ñтой наÑтройки."
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
-msgstr ""
-
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
+msgstr "Только подпиÑанные коммиты могут быть помещены в Ñтот репозиторий."
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
-msgstr ""
+msgstr "Эта наÑтройка применÑетÑÑ Ð½Ð° уровне Ñервера и может быть переопределена админиÑтратором."
msgid "ProjectSettings|This setting is applied on the server level but has been overridden for this project."
-msgstr ""
+msgstr "Эта наÑтройка применÑетÑÑ Ð½Ð° уровне Ñервера, но была переопределена Ð´Ð»Ñ Ñтого проекта."
msgid "ProjectSettings|This setting will be applied to all projects unless overridden by an admin."
-msgstr ""
+msgstr "Эта наÑтройка будет применена Ð´Ð»Ñ Ð²Ñех проектов, еÑли иное поведение не переопределено админиÑтратором."
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr ""
+msgstr "Пользователи могут отправлÑÑ‚ÑŒ коммиты в данный репозиторий, только в Ñлучае еÑли коммит подпиÑан одним из подтвержденных адреÑов почты."
msgid "Projects"
msgstr "Проекты"
@@ -2006,36 +2528,39 @@ msgid "ProjectsDropdown|This feature requires browser localStorage support"
msgstr "Эта функциональноÑÑ‚ÑŒ требует поддержки localStorage в вашем браузере"
msgid "PrometheusService|By default, Prometheus listens on ‘http://localhost:9090’. It’s not recommended to change the default address and port as this might affect or conflict with other services running on the GitLab server."
-msgstr ""
+msgstr "По умолчанию, Prometheus запуÑкаетÑÑ Ð¿Ð¾ адреÑу ‘http://localhost:9090’. Ðе рекомендуетÑÑ Ð¸Ð·Ð¼ÐµÐ½ÑÑ‚ÑŒ Ð°Ð´Ñ€ÐµÑ Ð¸ порт по умолчанию, так как Ñто может привеÑти к конфликту Ñ Ð´Ñ€ÑƒÐ³Ð¸Ð¼ ÑервиÑами запущенными на GitLab Ñервере."
msgid "PrometheusService|Finding and configuring metrics..."
-msgstr ""
+msgstr "Определение и наÑтройка метрик..."
msgid "PrometheusService|Metrics"
-msgstr ""
+msgstr "Метрики"
msgid "PrometheusService|Metrics are automatically configured and monitored based on a library of metrics from popular exporters."
-msgstr ""
+msgstr "Метрики автоматичеÑки наÑтраиваютÑÑ Ð¸ отÑлеживаютÑÑ Ð½Ð° оÑнове популÑрных библиотек метрик."
msgid "PrometheusService|Missing environment variable"
-msgstr ""
+msgstr "Пропущена Ð¿ÐµÑ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð¾ÐºÑ€ÑƒÐ¶ÐµÐ½Ð¸Ñ"
msgid "PrometheusService|Monitored"
-msgstr ""
+msgstr "Мониторинг подключен"
msgid "PrometheusService|More information"
-msgstr ""
+msgstr "Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
msgid "PrometheusService|No metrics are being monitored. To start monitoring, deploy to an environment."
-msgstr ""
+msgstr "Ðи одной метрики не отÑлеживаетÑÑ. Ð”Ð»Ñ Ð½Ð°Ñ‡Ð°Ð»Ð° мониторинга разверните окружение."
msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example.com/"
-msgstr ""
+msgstr "Базовый Ð°Ð´Ñ€ÐµÑ Prometheus API, например http://prometheus.example.com/"
msgid "PrometheusService|Time-series monitoring service"
msgstr ""
msgid "PrometheusService|View environments"
+msgstr "ПроÑмотр окружений"
+
+msgid "Protip:"
msgstr ""
msgid "Public - The group and any public projects can be viewed without any authentication."
@@ -2045,12 +2570,15 @@ msgid "Public - The project can be accessed without any authentication."
msgstr "Публичный - ДоÑтуп к проекту возможен без какой-либо проверки подлинноÑти."
msgid "Push Rules"
-msgstr ""
+msgstr "Правила Отправки"
msgid "Push events"
msgstr "Ð¡Ð¾Ð±Ñ‹Ñ‚Ð¸Ñ Ð¾Ñ‚Ð¿Ñ€Ð°Ð²ÐºÐ¸"
msgid "PushRule|Committer restriction"
+msgstr "ÐžÐ³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¼Ð¸Ñ‚ÐµÑ€Ð°"
+
+msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
msgid "Read more"
@@ -2065,9 +2593,15 @@ msgstr "Ветки"
msgid "RefSwitcher|Tags"
msgstr "Теги"
-msgid "Registry"
+msgid "Reference:"
msgstr ""
+msgid "Register / Sign In"
+msgstr ""
+
+msgid "Registry"
+msgstr "РееÑÑ‚Ñ€"
+
msgid "Related Commits"
msgstr "СвÑзанные коммиты"
@@ -2089,9 +2623,18 @@ msgstr "СвÑзанные Влитые ЗапроÑÑ‹"
msgid "Remind later"
msgstr "Ðапомнить позже"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "Удалить проект"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "Репозиторий"
@@ -2107,6 +2650,12 @@ msgstr "СброÑить ключ доÑтупа проверки работоÑ
msgid "Reset runners registration token"
msgstr "СброÑить ключ региÑтрации Gitlab Runners"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Revert this commit"
msgstr "Отменить Ñто коммит"
@@ -2116,15 +2665,15 @@ msgstr "Отменить Ñтот Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
msgid "SSH Keys"
msgstr "SSH Ключи"
-msgid "Save"
-msgstr "Сохранить"
-
msgid "Save changes"
msgstr "Сохранить изменениÑ"
msgid "Save pipeline schedule"
msgstr "Сохранить раÑпиÑание Ñборочной лини"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "РаÑпиÑание новой Ñборочной линии"
@@ -2135,43 +2684,64 @@ msgid "Scheduling Pipelines"
msgstr "Планирование Сборочных Линий"
msgid "Scoped issue boards"
-msgstr ""
+msgstr "ТематичеÑкие доÑки обÑуждений"
msgid "Search branches and tags"
msgstr "Ðайти ветки и теги"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "Секунд до очиÑтки информации о ÑбоÑÑ…"
-msgid "Seconds to wait after a storage failure"
-msgstr "Секунд задержки поÑле ÑÐ±Ð¾Ñ Ð² хранилище"
-
msgid "Seconds to wait for a storage access attempt"
msgstr "Секунд задержки между попытками доÑтупа к хранилищу"
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Выбрать формат архива"
msgid "Select a timezone"
msgstr "Выбор временной зоны"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "Выбор целевой ветки"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr "Сент."
msgid "September"
msgstr "СентÑбрь"
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr "Шаблоны Служб"
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "УÑтановите пароль в Ñвоем аккаунте, чтобы отправлÑÑ‚ÑŒ или получать код через %{protocol}."
-msgid "Set up CI"
-msgstr "ÐаÑтройка CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "ÐаÑтройка Koding"
@@ -2185,6 +2755,15 @@ msgstr "уÑтановите пароль"
msgid "Settings"
msgstr "ÐаÑтройки"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr "Показать родительÑкие Ñтраницы"
@@ -2198,27 +2777,33 @@ msgstr[1] "Показано %d Ñобытий"
msgstr[2] "Показано %d Ñобытий"
msgid "Sidebar|Change weight"
-msgstr ""
-
-msgid "Sidebar|Edit"
-msgstr ""
+msgstr "Изменить веÑ"
msgid "Sidebar|No"
-msgstr ""
+msgstr "Ðет"
msgid "Sidebar|None"
-msgstr ""
+msgstr "ОтÑутÑтвует"
msgid "Sidebar|Weight"
-msgstr ""
+msgstr "ВеÑ"
msgid "Snippets"
msgstr "Сниппеты"
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr "У Ð½Ð°Ñ Ñ‡Ñ‚Ð¾-то пошло не так."
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
+msgstr "Что-то пошло не так при попытке Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ ÑоÑтоÑÐ½Ð¸Ñ Ð±Ð»Ð¾ÐºÐ¸Ñ€Ð¾Ð²ÐºÐ¸ ${this.issuableDisplayName}"
+
+msgid "Something went wrong when toggling the button"
msgstr ""
msgid "Something went wrong while fetching the projects."
@@ -2227,6 +2812,9 @@ msgstr "Что-то пошло не так при получении проекÑ
msgid "Something went wrong while fetching the registry list."
msgstr "Что-то пошло не так при получении ÑпиÑка рееÑтров."
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "Сортировать по"
@@ -2270,7 +2858,7 @@ msgid "SortOptions|Least popular"
msgstr "Ðаименее популÑрный"
msgid "SortOptions|Less weight"
-msgstr ""
+msgstr "Меньший веÑ"
msgid "SortOptions|Milestone"
msgstr "Веха"
@@ -2282,7 +2870,7 @@ msgid "SortOptions|Milestone due soon"
msgstr "Веха, наÑÑ‚ÑƒÐ¿Ð°ÑŽÑ‰Ð°Ñ Ñ€Ð°Ð½ÑŒÑˆÐµ"
msgid "SortOptions|More weight"
-msgstr ""
+msgstr "Больший веÑ"
msgid "SortOptions|Most popular"
msgstr "Ðаиболее популÑрный"
@@ -2324,7 +2912,7 @@ msgid "SortOptions|Start soon"
msgstr "Ðачатые недавно"
msgid "SortOptions|Weight"
-msgstr ""
+msgstr "ВеÑ"
msgid "Source"
msgstr "ИÑточник"
@@ -2333,7 +2921,7 @@ msgid "Source code"
msgstr "ИÑходный код"
msgid "Source is not available"
-msgstr ""
+msgstr "ИÑходный текÑÑ‚ недоÑтупен"
msgid "Spam Logs"
msgstr "Спам Логи"
@@ -2354,14 +2942,14 @@ msgid "Start the Runner!"
msgstr "ЗапуÑтить GitLab Runner!"
msgid "Stopped"
+msgstr "ОÑтановлен"
+
+msgid "Storage"
msgstr ""
msgid "Subgroups"
msgstr "Подгруппы"
-msgid "Subscribe"
-msgstr "ПодпиÑатьÑÑ"
-
msgid "Switch branch/tag"
msgstr "Переключить ветка/тег"
@@ -2378,52 +2966,52 @@ msgid "Tags"
msgstr "Теги"
msgid "TagsPage|Browse commits"
-msgstr ""
+msgstr "ПроÑмотреть коммиты"
msgid "TagsPage|Browse files"
-msgstr ""
+msgstr "ПроÑмотреть файлы"
msgid "TagsPage|Can't find HEAD commit for this tag"
-msgstr ""
+msgstr "Ðевозможно найти HEAD коммит Ð´Ð»Ñ Ñтого тега"
msgid "TagsPage|Cancel"
-msgstr ""
+msgstr "Отмена"
msgid "TagsPage|Create tag"
-msgstr ""
+msgstr "Создать тег"
msgid "TagsPage|Delete tag"
-msgstr ""
+msgstr "Удалить тег"
msgid "TagsPage|Deleting the %{tag_name} tag cannot be undone. Are you sure?"
-msgstr ""
+msgstr "Удаление тега %{tag_name} не может быть отменено. Вы уверены?"
msgid "TagsPage|Edit release notes"
-msgstr ""
+msgstr "Редактировать заметки к релизу"
msgid "TagsPage|Existing branch name, tag, or commit SHA"
-msgstr ""
+msgstr "Ð˜Ð¼Ñ ÑущеÑтвующей ветки, тега или SHA коммита"
msgid "TagsPage|Filter by tag name"
-msgstr ""
+msgstr "Фильтр по имени тега"
msgid "TagsPage|New Tag"
-msgstr ""
+msgstr "Ðовый Тег"
msgid "TagsPage|New tag"
-msgstr ""
+msgstr "Ðовый тег"
msgid "TagsPage|Optionally, add a message to the tag."
-msgstr ""
+msgstr "При желании вы можете добавить Ñообщение в тег."
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
-msgstr ""
+msgstr "Опционально, добавьте опиÑание релиза к тегу. Оно будет Ñохранено в базе данных GitLab и отобразитÑÑ Ð½Ð° Ñтранице тегов."
msgid "TagsPage|Release notes"
-msgstr ""
+msgstr "Заметки к релизу"
msgid "TagsPage|Repository has no tags yet."
-msgstr ""
+msgstr "Репозитарий не Ñодержит тегов."
msgid "TagsPage|Sort by"
msgstr "Сортировать по"
@@ -2432,19 +3020,19 @@ msgid "TagsPage|Tags"
msgstr "Теги"
msgid "TagsPage|Tags give the ability to mark specific points in history as being important"
-msgstr ""
+msgstr "Теги дают возможноÑÑ‚ÑŒ отмечать определенные точки в иÑтории как важные"
msgid "TagsPage|This tag has no release notes."
-msgstr ""
+msgstr "Этот тег не Ñодержит заметок к релизу."
msgid "TagsPage|Use git tag command to add a new one:"
-msgstr ""
+msgstr "ИÑпользуйте команду git tag, чтобы добавить новый тег:"
msgid "TagsPage|Write your release notes or drag files here..."
-msgstr ""
+msgstr "Ðапишите Ñвои заметки к релизу или перетащите файлы Ñюда..."
msgid "TagsPage|protected"
-msgstr ""
+msgstr "защищенный"
msgid "Target Branch"
msgstr "Ветка"
@@ -2453,13 +3041,16 @@ msgid "Team"
msgstr "Команда"
msgid "Thanks! Don't show me this again"
-msgstr ""
+msgstr "СпаÑибо! Больше не показывайте мне Ñто Ñообщение"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
+msgstr "РаÑширенный глобальный поиÑк в GitLab - Ñто мощный инÑтрумент Ð´Ð»Ñ Ð¿Ð¾Ð¸Ñка, который Ñокращает ваше времÑ. ВмеÑто ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð´ÑƒÐ±Ð»Ð¸Ñ€ÑƒÑŽÑ‰ÐµÐ³Ð¾ кода и траты времени, вы можете иÑкать код внутри других команд, который поможет вам в вашем проекте."
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
-msgstr "Порог ÑÑ€Ð°Ð±Ð°Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ð¡ircuitBreaker должен быть меньше, чем порог ÑÑ€Ð°Ð±Ð°Ñ‚Ñ‹Ð²Ð°Ð½Ð¸Ñ Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð¸Ñ ÑбоÑ"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "Этап напиÑÐ°Ð½Ð¸Ñ ÐºÐ¾Ð´Ð° показывает Ð²Ñ€ÐµÐ¼Ñ Ñ Ð¿ÐµÑ€Ð²Ð¾Ð³Ð¾ коммита до ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа на ÑлиÑние. Данные автоматичеÑки добавÑÑ‚ÑÑ Ñюда поÑле того, как вы Ñоздать Ñвой первый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние."
@@ -2473,21 +3064,18 @@ msgstr "СвÑзь Ñ Ð¾Ñ‚Ð²ÐµÑ‚Ð²Ð»ÐµÐ½Ð¸ÐµÐ¼ удалена."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "Ð¡Ñ‚Ð°Ð´Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ времÑ, которое потребуетÑÑ Ñ Ð¼Ð¾Ð¼ÐµÐ½Ñ‚Ð° ÑÐ¾Ð·Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð´Ð¾ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ Ð¾Ð±Ñуждению вехи, или Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð½Ð° вашу доÑку задач. Ðачните Ñоздавать обÑуждениÑ, чтобы увидеть ÑÐ²ÐµÐ´ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ñтой Ñтадии."
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "The number of attempts GitLab will make to access a storage."
msgstr "КоличеÑтво попыток, которые GitLab будет предпринимать Ð´Ð»Ñ Ð´Ð¾Ñтупа к хранилищу."
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
-msgstr "КоличеÑтво ошибок, поÑле которого GitLab начнёт временно отключать доÑтуп к шарду хранилища на хоÑте"
-
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "КоличеÑтво Ñбоев, поÑле которого Gitlab полноÑтью прекратит доÑтуп к хранилищу. Изменение Ñчётчика \"количеÑтво Ñбоев\" может быть произведено через админиÑтративный интерфейÑ: %{link_to_health_page} или при помощи API %{api_documentation_link}."
msgid "The phase of the development lifecycle."
msgstr "Фаза жизненного цикла разработки."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "РаÑпиÑание Ñборочных линий регулÑрно запуÑкает Ñборочные линии Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»ÐµÐ½Ð½Ñ‹Ñ… ветвей или тегов. Запланированные Ñборочные линии наÑледуют Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð½Ð° доÑтуп к проекту на оÑнове ÑвÑзанного Ñ Ð½Ð¸Ð¼Ð¸ пользователÑ."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "Этап Ð¿Ð»Ð°Ð½Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ð¿Ð¾ÐºÐ°Ð·Ñ‹Ð²Ð°ÐµÑ‚ Ð²Ñ€ÐµÐ¼Ñ Ð¾Ñ‚ предыдущего шага до отправки первого коммита. ДобавлÑетÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡ÐµÑки, как только отправите Ñвой первый коммит."
@@ -2518,20 +3106,47 @@ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² Ñекундах, в течение которого GitLa
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð² Ñекундах в течении которого GitLab будет пытатьÑÑ Ð¿Ð¾Ð»ÑƒÑ‡Ð¸Ñ‚ÑŒ доÑтуп к хранилищу. ПоÑле Ñтого времени будет зафикÑирована ошибка Ð¿Ñ€ÐµÐ²Ñ‹ÑˆÐµÐ½Ð¸Ñ Ð²Ñ€ÐµÐ¼ÐµÐ½Ð¸ ожиданиÑ."
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "ВремÑ, затраченное каждым Ñлементом, Ñобранным на Ñтом Ñтапе."
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Среднее значение в Ñ€Ñду. Пример: между 3, 5, 9, Ñреднее 5, между 3, 5, 7, 8, Ñреднее (5+7)/2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "Проблемы Ñ Ð´Ð¾Ñтупом к Git хранилищу: "
-msgid "This board\\'s scope is reduced"
+msgid "There was an error loading users activity calendar."
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
-msgstr "Эта ветка была изменена, пока вы её редактировали. Ð’Ñ‹ хотите Ñоздать новую ветку?"
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
+msgid "This board\\'s scope is reduced"
+msgstr "УÑтановлен отбор"
+
+msgid "This directory"
+msgstr ""
msgid "This is a confidential issue."
msgstr "Это конфиденциальное обÑуждение."
@@ -2539,21 +3154,48 @@ msgstr "Это конфиденциальное обÑуждение."
msgid "This is the author's first Merge Request to this project."
msgstr "Это первый Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние от автора в Ñтот проект."
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr "Это обÑуждение конфиденциально и заблокировано."
msgid "This issue is locked."
msgstr "ОбÑуждение заблокировано."
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Это означает, что вы не можете отправить код, пока не Ñоздадите пуÑтой репозиторий или не импортируете ÑущеÑтвующий."
msgid "This merge request is locked."
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние заблокирован."
-msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
msgstr ""
+msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
+msgstr "Эти Ñлектронные пиÑьма автоматичеÑки преобразуютÑÑ Ð² обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ (комментарии раÑÑылаютÑÑ ÐºÐ°Ðº ветвь обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² почте), перечиÑленные здеÑÑŒ."
+
msgid "Time before an issue gets scheduled"
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ начала Ð¿Ð¾Ð¿Ð°Ð´Ð°Ð½Ð¸Ñ Ð¾Ð±ÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð² планировщик"
@@ -2563,9 +3205,21 @@ msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ начала работы над обÑуждением"
msgid "Time between merge request creation and merge/close"
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð¼ÐµÐ¶Ð´Ñƒ Ñозданием запроÑа ÑлиÑÐ½Ð¸Ñ Ð¸ ÑлиÑнием / закрытием"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾ первого запроÑа на ÑлиÑние"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "%s дней назад"
@@ -2705,52 +3359,85 @@ msgid "Time|s"
msgstr "Ñ"
msgid "Title"
+msgstr "Заголовок"
+
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
msgstr ""
msgid "Total Time"
msgstr "Общее времÑ"
msgid "Total issue time spent"
-msgstr ""
+msgstr "Общее времÑ, затраченное на обÑуждение"
msgid "Total test time for all commits/merges"
msgstr "Общее Ð²Ñ€ÐµÐ¼Ñ Ñ‚ÐµÑÑ‚Ð¸Ñ€Ð¾Ð²Ð°Ð½Ð¸Ñ Ñ„Ð¸ÐºÑаций/ÑлиÑний"
msgid "Track activity with Contribution Analytics."
-msgstr ""
+msgstr "ОтÑлеживать активноÑÑ‚ÑŒ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ðналитики УчаÑтников."
msgid "Track groups of issues that share a theme, across projects and milestones"
+msgstr "Следите за обÑуждениÑми, Ñгруппированными по темам, Ñразу из неÑкольких проектов и Ñтапов"
+
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
msgstr ""
msgid "Turn on Service Desk"
+msgstr "Включить Службу Поддержки"
+
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
msgstr ""
msgid "Unlock"
msgstr "Разблокировать"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Разблокировано"
msgid "Unstar"
msgstr "СнÑÑ‚ÑŒ отметку"
-msgid "Unsubscribe"
-msgstr "ОтпиÑатьÑÑ"
+msgid "Up to date"
+msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
-msgstr ""
+msgstr "ПовыÑьте ваш тарифный план, чтобы активировать Улучшенный Глобальный ПоиÑк."
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr ""
+msgstr "ПовыÑьте ваш тарифный план, чтобы активировать Ðналитики УчаÑтников."
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr ""
+msgstr "ПовыÑьте ваш тарифный план, чтобы активировать Групповые Веб-Обработчики."
msgid "Upgrade your plan to activate Issue weight."
-msgstr ""
+msgstr "ПовыÑьте ваш тарифный план чтобы активировать Ð²ÐµÑ Ð¾Ð±Ñуждений."
msgid "Upgrade your plan to improve Issue boards."
-msgstr ""
+msgstr "ПовыÑьте ваш тарифный план, чтобы улучшить доÑки обÑуждений."
msgid "Upload New File"
msgstr "Загрузить новый файл"
@@ -2758,11 +3445,14 @@ msgstr "Загрузить новый файл"
msgid "Upload file"
msgstr "Загрузить файл"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "кликните Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸"
msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab"
-msgstr ""
+msgstr "ИÑпользуйте Службу поддержки Ð´Ð»Ñ ÑвÑзи Ñ Ð²Ð°ÑˆÐ¸Ð¼Ð¸ пользователÑми (например, Ð´Ð»Ñ Ð¾ÑущеÑÑ‚Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð´Ð´ÐµÑ€Ð¶ÐºÐ¸ клиентов) через Ñлектронную почту непоÑредÑтвенно в GitLab"
msgid "Use the following registration token during setup:"
msgstr "ИÑпользуйте Ñледующий токен региÑтрации в процеÑÑе уÑтановки:"
@@ -2770,9 +3460,15 @@ msgstr "ИÑпользуйте Ñледующий токен региÑтрацÐ
msgid "Use your global notification setting"
msgstr "ИÑпользуютÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ‹Ð¹ наÑтройки уведомлений"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr "ПроÑмотр файла @ "
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "ПроÑмотреть открытый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
@@ -2794,20 +3490,20 @@ msgstr "Ðе определен"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Хотите увидеть данные? ОбратитеÑÑŒ к админиÑтратору за доÑтупом."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "Ð˜Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð¿Ð¾ Ñтапу отÑутÑтвует."
msgid "We want to be sure it is you, please confirm you are not a robot."
-msgstr ""
+msgstr "Мы хотим быть уверены, что Ñто вы, пожалуйÑта, подтвердите, что вы не робот."
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr ""
+msgstr "Веб-обработчики позволÑÑŽÑ‚ вам вызывать Ð°Ð´Ñ€ÐµÑ URL еÑли, например, отправлен новый код или Ñоздано новое обÑуждение. Ð’Ñ‹ можете наÑтроить веб-обработчики так, чтобы они реагировали на определённые ÑобытиÑ, такие как отправки кода, обÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ запроÑÑ‹ на ÑлиÑние. Групповые веб-обработчики применÑÑŽÑ‚ÑÑ ÐºÐ¾ вÑем проектам в группе и позволÑÑŽÑ‚ вам Ñтандартизовать функциональноÑÑ‚ÑŒ веб-обработчиков Ð´Ð»Ñ Ð²Ñей вашей группы."
msgid "Weight"
-msgstr ""
-
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr "Когда доÑтуп к хранилищу получить не удалоÑÑŒ, GitLab приоÑтановит доÑтуп к хранилищу на времÑ, указанное здеÑÑŒ. Это позволит файловой ÑиÑтеме воÑÑтановитьÑÑ. Репозитории на Ñбойных \"шардах\" будут временно недоÑтупны"
+msgstr "ВеÑ"
msgid "Wiki"
msgstr "Wiki"
@@ -2827,6 +3523,12 @@ msgstr "РекомендуетÑÑ ÑƒÑтановить %{markdown}, чтобы
msgid "WikiClone|Start Gollum and edit locally"
msgstr "ЗапуÑтите Gollum и редактируете локально"
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr "Ð’Ñ‹ не можете Ñоздавать вики-Ñтраницы"
@@ -2912,7 +3614,7 @@ msgid "Wiki|Wiki Pages"
msgstr "Вики Страницы"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr ""
+msgstr "С аналитикой учаÑтников вы можете изучать активноÑÑ‚ÑŒ в обÑуждениÑÑ…, запроÑах на ÑлиÑние и Ñобытий отправки кода Ð´Ð»Ñ Ð²Ð°ÑˆÐµÐ¹ организации и её учаÑтников."
msgid "Withdraw Access Request"
msgstr "Отменить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа"
@@ -2929,12 +3631,24 @@ msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ удалить ÑвÑзь ответвлен
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Ð’Ñ‹ ÑобираетеÑÑŒ передать проект %{project_name_with_namespace} другому владельцу. Ð’Ñ‹ ÐБСОЛЮТÐО уверены?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "Ð’Ñ‹ можете добавлÑÑ‚ÑŒ только файлы, когда находитеÑÑŒ в ветке"
-msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgid "You can only edit files when you are on a branch"
msgstr ""
+msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
+msgstr "Ð’Ñ‹ не можете запиÑывать на подчиненные ÑкземплÑры \"только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ\" клаÑтера GitLab Geo. ИÑпользуйте вмеÑто Ñтого %{link_to_primary_node}."
+
msgid "You cannot write to this read-only GitLab instance."
msgstr "Ð’Ñ‹ не можете запиÑывать на Ñтот ÑкземплÑÑ€ \"только Ð´Ð»Ñ Ñ‡Ñ‚ÐµÐ½Ð¸Ñ\" клаÑтера GitLab."
@@ -2969,6 +3683,12 @@ msgid "You won't be able to pull or push project code via SSH until you %{add_ss
msgstr "Ð’Ñ‹ не Ñможете получать и отправлÑÑ‚ÑŒ код проекта через SSH пока %{add_ssh_key_link} в ваш профиль."
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr "Ð’Ñ‹ не Ñможете работать Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¾Ð¼ через SSH, пока не добавите в Ñвой профиль SSH ключ"
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
msgstr ""
msgid "Your comment will not be visible to the public."
@@ -2983,13 +3703,67 @@ msgstr "Ваше имÑ"
msgid "Your projects"
msgstr "Ваши проекты"
-msgid "branch name"
+msgid "assign yourself"
msgstr ""
+msgid "branch name"
+msgstr "Ð¸Ð¼Ñ Ð²ÐµÑ‚Ð²Ð¸"
+
msgid "by"
+msgstr "по"
+
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
msgstr ""
msgid "commit"
+msgstr "коммит"
+
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
msgstr ""
msgid "day"
@@ -2998,12 +3772,153 @@ msgstr[0] "день"
msgstr[1] "дней"
msgstr[2] "дней"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "новый Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
msgid "notification emails"
msgstr "email Ð´Ð»Ñ ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ð¹"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "иÑточник"
@@ -3016,12 +3931,21 @@ msgstr "пароль"
msgid "personal access token"
msgstr "токен Ð´Ð»Ñ Ð¿ÐµÑ€Ñонального доÑтупа"
+msgid "remove due date"
+msgstr ""
+
msgid "source"
+msgstr "иÑходный текÑÑ‚"
+
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
msgid "to help your contributors communicate effectively!"
-msgstr ""
+msgstr "чтобы помочь учаÑтникам взаимодейÑтвовать Ñффективнее!"
msgid "username"
msgstr "Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index 0f20f0c9ceb..f775a511780 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 06:39-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 06:15-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Ukrainian\n"
"Language: uk_UA\n"
@@ -16,25 +16,46 @@ msgstr ""
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr " Ñ–"
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d коміт"
msgstr[1] "%d коміта"
msgstr[2] "%d комітів"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] "%d проблема"
+msgstr[1] "%d проблеми"
+msgstr[2] "%d проблем"
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d шар"
msgstr[1] "%d шари"
msgstr[2] "%d шарів"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] "%d запит на злиттÑ"
+msgstr[1] "%d запита на злиттÑ"
+msgstr[2] "%d запитів на злиттÑ"
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "%s доданий коміт був виключений Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ із швидкодією."
msgstr[1] "%s доданих коміта були виключені Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ із швидкодією."
msgstr[2] "%s доданих комітів були виключені Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð±Ñ–Ð³Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼ із швидкодією."
-msgid "%{commit_author_link} committed %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
msgstr "%{commit_author_link} закомітив %{commit_timeago}"
msgid "%{count} participant"
@@ -49,9 +70,6 @@ msgstr "на %{number_commits_behind} комітів позаду %{default_bran
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab надаÑÑ‚ÑŒ доÑтуп на наÑтупну Ñпробу."
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "%{number_of_failures} із %{maximum_failures} невдач. GitLab заблокує доÑтуп на %{number_of_seconds} Ñекунд."
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "%{number_of_failures} від %{maximum_failures} невдач. GitLab автоматично не повторюватиме Ñпробу. Скиньте інформацію Ñховища при уÑуненні проблеми."
@@ -113,13 +131,13 @@ msgid "Add"
msgstr "Додати"
msgid "Add Changelog"
-msgstr "Додати ÑпиÑок змін (Changelog)"
+msgstr "Додати ÑпиÑок змін"
msgid "Add Contribution guide"
-msgstr ""
+msgstr "Додати керівництво Ð´Ð»Ñ ÑƒÑ‡Ð°Ñників"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
-msgstr ""
+msgstr "Додайте групові веб-гуки та GitLab Enterprise Edition."
msgid "Add License"
msgstr "Додати ліцензію"
@@ -127,24 +145,81 @@ msgstr "Додати ліцензію"
msgid "Add new directory"
msgstr "Додати новий каталог"
+msgid "Add todo"
+msgstr "Додати задачу"
+
+msgid "AdminArea|Stop all jobs"
+msgstr "Зупинити вÑÑ– завданнÑ"
+
+msgid "AdminArea|Stop all jobs?"
+msgstr "Зупинити вÑÑ– завданнÑ?"
+
+msgid "AdminArea|Stop jobs"
+msgstr "Зупинити завданнÑ"
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr "Зупинка завдань пройшла невдало"
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "Ñторінка ÑтатуÑу"
+msgid "Advanced"
+msgstr "Розширений"
+
msgid "Advanced settings"
msgstr "Додаткові параметри"
msgid "All"
msgstr "Ð’ÑÑ–"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð¼Ñ–Ð½Ð¸ підпиÑки на ÑповіщеннÑ"
msgid "An error occurred when updating the issue weight"
msgstr "Збій під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ð°Ð³Ð¸ проблеми"
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… Ð´Ð»Ñ Ð±Ñ–Ñ‡Ð½Ð¾Ñ— панелі"
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr "СталаÑÑŒ помилка. Спробуйте ще раз."
@@ -152,7 +227,7 @@ msgid "Appearance"
msgstr "Зовнішній виглÑд"
msgid "Applications"
-msgstr "Додатки"
+msgstr "ЗаÑтоÑунки"
msgid "Apr"
msgstr "квіт."
@@ -164,14 +239,11 @@ msgid "Archived project! Repository is read-only"
msgstr "Заархівований проект! Репозиторій доÑтупний лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ"
msgid "Are you sure you want to delete this pipeline schedule?"
-msgstr ""
+msgstr "Ви впевнені, що хочете видалити цей розклад Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ð°?"
msgid "Are you sure you want to discard your changes?"
msgstr "Ви впевнені, що бажаєте ÑкаÑувати ваші зміни?"
-msgid "Are you sure you want to leave this group?"
-msgstr "Ви впевнені що хочете залишити цю групу?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "Ви впевнені, що бажаєте Ñкинути реєÑтраційний токен?"
@@ -184,6 +256,21 @@ msgstr "Ви впевнені?"
msgid "Artifacts"
msgstr "Ðртефакти"
+msgid "Assign custom color like #FF0000"
+msgstr "Призначити влаÑний колір типу #FF0000"
+
+msgid "Assign labels"
+msgstr "Призначити мітку"
+
+msgid "Assign milestone"
+msgstr "Призначити етап"
+
+msgid "Assign to"
+msgstr "Призначити"
+
+msgid "Assignee"
+msgstr "Виконавець"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Прикріпити файл за допомогою перетÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ %{upload_link}"
@@ -199,15 +286,18 @@ msgstr "Журнал автентифікації"
msgid "Author"
msgstr "Ðвтор"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy необхідно вказати доменне Ñ–Ð¼â€™Ñ Ñ‚Ð° %{kubernetes}."
+msgid "Authors: %{authors}"
+msgstr "Ðвтори: %{authors}"
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy необхідно вказати доменне ім’Ñ."
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "Ð”Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Auto Review Apps та Auto Deploy необхіден %{kubernetes}."
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "Auto DevOps (бета)"
@@ -218,7 +308,7 @@ msgid "AutoDevOps|Enable in settings"
msgstr "Включити в налаштуваннÑÑ…"
msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
-msgstr "AutoDevOps буде автоматично збирати, теÑтувати та розгортати вашу програму на оÑнові визначеної CI/CD конфігурації."
+msgstr "AutoDevOps буде автоматично збирати, теÑтувати та розгортати ваш заÑтоÑунок на оÑнові визначеної CI/CD конфігурації."
msgid "AutoDevOps|Learn more in the %{link_to_documentation}"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ в %{link_to_documentation}"
@@ -229,6 +319,12 @@ msgstr "Ви можете активувати %{link_to_settings} Ð´Ð»Ñ Ñ†ÑŒÐ¾
msgid "Available"
msgstr "ДоÑтупний"
+msgid "Avatar will be removed. Are you sure?"
+msgstr "Ðватар буде видалено. Ви впевнені?"
+
+msgid "Average per day: %{average}"
+msgstr "Ð’ Ñередньому за день: %{average}"
+
msgid "Billing"
msgstr "Білінг"
@@ -281,6 +377,9 @@ msgid "BillingPlans|paid annually at %{price_per_year}"
msgstr "ОплачуєтьÑÑ Ñ‰Ð¾Ñ€Ñ–Ñ‡Ð½Ð¾ %{price_per_year}"
msgid "BillingPlans|per user"
+msgstr "за кориÑтувача"
+
+msgid "Begin with the selected commit"
msgstr ""
msgid "Branch"
@@ -305,7 +404,7 @@ msgid "BranchSwitcherPlaceholder|Search branches"
msgstr "Пошук гілок"
msgid "BranchSwitcherTitle|Switch branch"
-msgstr ""
+msgstr "Перейти в гілку"
msgid "Branches"
msgstr "Гілки"
@@ -412,8 +511,8 @@ msgstr "від"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI"
+msgid "CI/CD configuration"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
msgid "CICD|Jobs"
msgstr "ЗавданнÑ"
@@ -424,6 +523,9 @@ msgstr "СкаÑувати"
msgid "Cancel edit"
msgstr "Відмінити правку"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr "Вага зміни"
@@ -434,13 +536,19 @@ msgid "ChangeTypeActionLabel|Revert in branch"
msgstr "Ðнулювати у гілці"
msgid "ChangeTypeAction|Cherry-pick"
-msgstr ""
+msgstr "Вибрати (cherry-pick)"
msgid "ChangeTypeAction|Revert"
msgstr "Ðнулювати коміт"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
-msgstr "СпиÑок змін (Changelog)"
+msgstr "СпиÑок змін"
+
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
msgid "Charts"
msgstr "Графіки"
@@ -448,6 +556,9 @@ msgstr "Графіки"
msgid "Chat"
msgstr "Чат"
+msgid "Check interval"
+msgstr "Інтервал перевірки"
+
msgid "Checking %{text} availability…"
msgstr "Перевірка доÑтупноÑÑ‚Ñ– %{text}…"
@@ -455,12 +566,24 @@ msgid "Checking branch availability..."
msgstr "Перевірка доÑтупноÑÑ‚Ñ– гілки..."
msgid "Cherry-pick this commit"
-msgstr ""
+msgstr "Вибрати (cherry-pick) цей коміт"
msgid "Cherry-pick this merge request"
+msgstr "Вибрати (cherry-pick) цей запит на злиттÑ"
+
+msgid "Choose File ..."
+msgstr "Виберіть файл ..."
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr "Виберіть файл..."
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr ""
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -517,41 +640,77 @@ msgstr "пропущено"
msgid "CiStatus|running"
msgstr "виконуєтьÑÑ"
+msgid "CiVariables|Input variable key"
+msgstr "Ключ вхідної змінної"
+
+msgid "CiVariables|Input variable value"
+msgstr "Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ñ…Ñ–Ð´Ð½Ð¾Ñ— змінної"
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr "* (Ð’ÑÑ– Ñередовища)"
+
+msgid "CiVariable|All environments"
+msgstr "Ð’ÑÑ– Ñередовища"
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr "Ðове Ñередовище"
+
+msgid "CiVariable|Protected"
+msgstr "Захищений"
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr "Перевірка невдала"
+
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "circuitbreaker api"
+msgid "Click to expand text"
+msgstr "ÐатиÑніть, щоб розгорнути текÑÑ‚"
+
msgid "Clone repository"
msgstr "Клонувати репозиторій"
msgid "Close"
msgstr "Закрити"
-msgid "Cluster"
-msgstr "КлаÑтер"
-
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
-msgstr "%{appList} уÑпішно вÑтановлені на вашому клаÑтері"
+msgid "Closed"
+msgstr "Закрито"
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
-msgstr "%{boldNotice} Це додаÑÑ‚ÑŒ реÑурÑи (наприклад баланÑер навантаженнÑ), що Ñпричинить додаткові витрати. ПереглÑньте %{pricingLink}"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr "%{appList} були уÑпішно вÑтановлені на ваш Kubernetes-клаÑтер"
msgid "ClusterIntegration|API URL"
msgstr "API URL"
-msgid "ClusterIntegration|Active"
-msgstr "Ðктивний"
-
-msgid "ClusterIntegration|Add an existing cluster"
-msgstr "Додати Ñ–Ñнуючий клаÑтер"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr "Додати Kubernetes клаÑтер"
-msgid "ClusterIntegration|Add cluster"
-msgstr "Додати клаÑтер"
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr "Додати Ñ–Ñнуючий Kubernetes-клаÑтер"
-msgid "ClusterIntegration|All"
-msgstr "Ð’ÑÑ–"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr "Детальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із цим Kubernetes-клаÑтером"
msgid "ClusterIntegration|Applications"
-msgstr "Додатки"
+msgstr "ЗаÑтоÑунки"
+
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr "Ви впевнені, що хочете видалити інтеграцію із цим Kubernetes-клаÑтером? Це не призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñамого клаÑтера."
msgid "ClusterIntegration|CA Certificate"
msgstr "Сертифікат центру Ñертифікації"
@@ -559,38 +718,14 @@ msgstr "Сертифікат центру Ñертифікації"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "Ðабір Ñертифікатів (формат PEM)"
-msgid "ClusterIntegration|Choose how to set up cluster integration"
-msgstr "Виберіть ÑпоÑіб Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— з клаÑтером"
-
-msgid "ClusterIntegration|Cluster"
-msgstr "КлаÑтер"
-
-msgid "ClusterIntegration|Cluster details"
-msgstr "Параметри клаÑтера"
-
-msgid "ClusterIntegration|Cluster integration"
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· клаÑтером"
-
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· клаÑтером вимкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr "Виберіть ÑпоÑіб Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із Kubernetes-клаÑтером"
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· клаÑтером увімкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr "Виберіть Ñкі з Ñередовищ вашого проекту викориÑтовуватимуть цей Kubernetes-клаÑтер."
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr ""
-
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
-msgstr "СтворюєтьÑÑ ÐºÐ»Ð°Ñтер в Google Kubernetes Engine..."
-
-msgid "ClusterIntegration|Cluster name"
-msgstr "Ім'Ñ ÐºÐ»Ð°Ñтера"
-
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
-msgstr "КлаÑтер був уÑпішно Ñтворено в Google Kubernetes Engine. Оновіть Ñторінку, щоб переглÑнути додатову інформацію"
-
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr "КлаÑтери дозволÑÑŽÑ‚ÑŒ вам викориÑтовувати Review Apps, розгортати ваші програми, запуÑкати ваші конвеєри Ñ– багато іншого проÑтим ÑпоÑобом. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgstr "Керуйте ÑпоÑобом інтеграції вашого Kubernetes-клаÑтера з GitLab"
msgid "ClusterIntegration|Copy API URL"
msgstr "Скопіювати URL API"
@@ -598,38 +733,35 @@ msgstr "Скопіювати URL API"
msgid "ClusterIntegration|Copy CA Certificate"
msgstr "Скопіювати Ñертифікат центру Ñертифікації"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr "Скопіювати Ñ–Ð¼â€™Ñ Kubernetes-клаÑтера"
+
msgid "ClusterIntegration|Copy Token"
msgstr "Скопіювати Токен"
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "Копіювати назву клаÑтера"
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr "Створити Kubernetes-клаÑтер"
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
-msgstr "Створити новий клаÑтер у Google Engine прÑмо з GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgstr "Створити Kubernetes-клаÑтер на Google Kubernetes Engine"
-msgid "ClusterIntegration|Create cluster"
-msgstr "Створити клаÑтер"
-
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
-msgstr "Створити клаÑтер в Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgstr "Створити Kubernetes-клаÑтер на Google Kubernetes Engine прÑмо із GitLab"
msgid "ClusterIntegration|Create on GKE"
msgstr "Створити в GKE"
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "Увімкнути інтеграцію із клаÑтерами"
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr "Вкажіть параметри Ñ–Ñнуючого клаÑтера Kubernetes"
-msgid "ClusterIntegration|Enter the details for your cluster"
-msgstr "Введіть докладний Ð¾Ð¿Ð¸Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ клаÑтера"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr "Введіть параметри вашого Kubernetes-клаÑтера"
-msgid "ClusterIntegration|Environment pattern"
-msgstr "Шаблон Ñередовища"
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
-msgid "ClusterIntegration|GKE pricing"
-msgstr "ВартіÑÑ‚ÑŒ GKE"
+msgid "ClusterIntegration|GitLab Integration"
+msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ð· GitLab"
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
@@ -646,47 +778,83 @@ msgstr "Проект Google Kubernetes Engine"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
-msgid "ClusterIntegration|Inactive"
-msgstr "Ðеактивні"
-
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
msgid "ClusterIntegration|Install"
msgstr "Ð’Ñтановити"
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr "Ð’Ñтановіть додатки у ваш клаÑтер. Докладніше про %{helpLink}"
-
msgid "ClusterIntegration|Installed"
msgstr "Ð’Ñтановлений"
msgid "ClusterIntegration|Installing"
msgstr "Ð’ÑтановленнÑ"
-msgid "ClusterIntegration|Integrate cluster automation"
-msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ ÐºÐ»Ð°Ñтерної автоматизації"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ—"
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr "Kubernetes-клаÑтер"
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr "Параметри Kubernetes-клаÑтера"
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером"
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером вимкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ Ñ–Ð· Kubernetes-клаÑтером увімкнена Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr "Ð†Ð¼â€™Ñ Kubernetes-клаÑтера"
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{link_to_documentation}"
-msgid "ClusterIntegration|Learn more about Clusters"
-msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про клаÑтери"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Kubernetes"
+
+msgid "ClusterIntegration|Learn more about environments"
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
msgid "ClusterIntegration|Machine type"
msgstr "Тип машини"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "ПереконайтеÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ %{link_to_requirements} Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ð°Ñтерів"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
+
+msgid "ClusterIntegration|Manage"
+msgstr "УправліннÑ"
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
-msgstr "Ð£Ð¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ”ÑŽ із клаÑтером у вашому Gitlab-проекті"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "Ð”Ð»Ñ ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ñвоїм клаÑтером перейдіть на %{link_gke}"
+msgid "ClusterIntegration|More information"
+msgstr "Додаткова інформаціÑ"
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
-msgstr "Кілька клаÑтерів доÑтупні в GitLab Enterprise Edition Premium Ñ– Ultimate"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
msgid "ClusterIntegration|Note:"
msgstr "Примітка:"
@@ -694,38 +862,35 @@ msgstr "Примітка:"
msgid "ClusterIntegration|Number of nodes"
msgstr "КількіÑÑ‚ÑŒ вузлів"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
-msgstr "Введіть інформацію про доÑтуп до Ñвого клаÑтера. Якщо вам потрібна допомога, ви можете прочитати наші %{link_to_help_page} по клаÑтерам"
-
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr "Проблема Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ»Ð°Ñтера"
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr "Проблема Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑпиÑку клаÑтерів"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgstr "Будь-лаÑка впевнітьÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Google задовольнÑÑ” наÑтупним вимогам:"
msgid "ClusterIntegration|Project ID"
-msgstr ""
+msgstr "Ідентифікатор проекту"
msgid "ClusterIntegration|Project namespace"
-msgstr ""
+msgstr "ПроÑÑ‚Ñ–Ñ€ імен проекту"
msgid "ClusterIntegration|Project namespace (optional, unique)"
-msgstr ""
+msgstr "ПроÑÑ‚Ñ–Ñ€ імен проекту (не обов’Ñзковий, унікальний)"
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
-msgstr "Прочитайте нашу документацію %{link_to_help_page} по інтеграції із клаÑтером."
+msgid "ClusterIntegration|Prometheus"
+msgstr "Prometheus"
-msgid "ClusterIntegration|Remove cluster integration"
-msgstr "Видалити інтеграцію з клаÑтером"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr "Відалити інтеграцію із Kubernetes-клаÑтером"
msgid "ClusterIntegration|Remove integration"
msgstr "Видалити інтеграцію"
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
-msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ»Ð°Ñтерної інтеграції призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ñ–Ð³ÑƒÑ€Ð°Ñ†Ñ–Ñ— клаÑтера, Ñку ви додали до цього проекту. Ð¦Ñ Ð´Ñ–Ñ Ð½Ðµ буде видалÑти ваш клаÑтер у Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Запит про початок вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ виконано"
@@ -733,8 +898,8 @@ msgstr "Запит про початок вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ викоÐ
msgid "ClusterIntegration|Save changes"
msgstr "Зберегти зміни"
-msgid "ClusterIntegration|See and edit the details for your cluster"
-msgstr "ПереглÑнути та редагувати параметри вашого клаÑтера"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|See machine types"
msgstr "ПереглÑнути типи машин"
@@ -754,26 +919,26 @@ msgstr "Показати"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "ЩоÑÑŒ пішло не так з нашого боку."
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
-msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ð°Ñтера в Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ %{title} ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
-msgid "ClusterIntegration|There are no clusters to show"
-msgstr "Ðемає клаÑтерів Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
-msgstr "Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¼Ð°Ñ” мати дозволи Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÐºÐ»Ð°Ñтера в %{link_to_container_project}, зазначеному нижче"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr "Увімкнути/вимкнути Kubernetes-клаÑтер"
-msgid "ClusterIntegration|Toggle Cluster"
-msgstr "Переключити КлаÑтер"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr "Увімкнути/вимкнути Kubernetes-клаÑтер"
msgid "ClusterIntegration|Token"
msgstr "Токен"
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "За допомогою підключеного до цього проекту клаÑтера, ви можете викориÑтовувати Review Apps, розгортати ваші проекти, запуÑкати конвеєри збірки та багато іншого."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ мати %{link_to_kubernetes_engine}"
@@ -784,17 +949,17 @@ msgstr "Зона"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr "доÑтуп до Google Kubernetes Engine"
-msgid "ClusterIntegration|cluster"
-msgstr "клаÑтер"
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
msgid "ClusterIntegration|documentation"
-msgstr "документаціÑ"
+msgstr "документації"
msgid "ClusterIntegration|help page"
msgstr "Ñторінка допомоги"
msgid "ClusterIntegration|installing applications"
-msgstr "вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÑ–Ð²"
+msgstr "вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°ÑтоÑунків"
msgid "ClusterIntegration|meets the requirements"
msgstr "задовольнÑÑ” вимогам"
@@ -802,6 +967,9 @@ msgstr "задовольнÑÑ” вимогам"
msgid "ClusterIntegration|properly configured"
msgstr "правильно налаштований"
+msgid "Collapse"
+msgstr "Згорнути"
+
msgid "Comments"
msgstr "Коментарі"
@@ -820,6 +988,9 @@ msgstr "ТриваліÑÑ‚ÑŒ оÑтанніх 30 комітів у хвилинÐ
msgid "Commit message"
msgstr "Коміт-повідомленнÑ"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "Коміт"
@@ -832,15 +1003,57 @@ msgstr "Коміти"
msgid "Commits feed"
msgstr "Канал комітів"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr "Коміт: %{commitText}"
+
msgid "Commits|History"
msgstr "ІÑторіÑ"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "Коміт від"
msgid "Compare"
msgstr "ПорівнÑти"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr "ПорівнÑÐ½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ†Ñ–Ð¹"
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr "ПорівнÑти"
+
+msgid "CompareBranches|Source"
+msgstr "Джерело"
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr "КонфіденційніÑÑ‚ÑŒ"
+
msgid "Container Registry"
msgstr "РеєÑÑ‚Ñ€ Контейнерів"
@@ -878,7 +1091,7 @@ msgid "ContainerRegistry|Tag"
msgstr "Тег"
msgid "ContainerRegistry|Tag ID"
-msgstr ""
+msgstr "Ідентифікатор тегу"
msgid "ContainerRegistry|Use different image names"
msgstr "ВикориÑтовуйте різні імена образів"
@@ -887,13 +1100,16 @@ msgid "ContainerRegistry|With the Docker Container Registry integrated into GitL
msgstr "За допомогою вбудованого в GitLab реєÑтру Docker контейнерів кожен проект може мати влаÑне міÑце Ð´Ð»Ñ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Docker образів."
msgid "Contribution guide"
-msgstr ""
+msgstr "ІнÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ Ð´Ð»Ñ ÑƒÑ‡Ð°Ñників"
msgid "Contributors"
msgstr "Контриб’ютори"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr "%{startDate} – %{endDate}"
+
msgid "ContributorsPage|Building repository graph."
-msgstr ""
+msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñхеми репозиторію."
msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limited to 6,000 commits."
msgstr "Коміти в %{branch_name}, за винÑтком комітів злиттÑ. Обмежено 6000 комітів."
@@ -913,14 +1129,23 @@ msgstr "Скопіюйте відкритий SSH-ключ в буфер обмÑ
msgid "Copy URL to clipboard"
msgstr "Скопіювати URL в буфер обміну"
+msgid "Copy branch name to clipboard"
+msgstr "Скопіювати назву гілки в буфер обміну"
+
msgid "Copy commit SHA to clipboard"
msgstr "Скопіювати ідентифікатор в буфер обміну"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr "Створити"
+
msgid "Create New Directory"
msgstr "Створити новий каталог"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
-msgstr ""
+msgstr "Створіть токен доÑтупу Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ аккаунта, щоб відправлÑти та отримувати через %{protocol}."
msgid "Create directory"
msgstr "Створити каталог"
@@ -934,6 +1159,9 @@ msgstr "Створити епік"
msgid "Create file"
msgstr "Створити файл"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "Створити запит на злиттÑ"
@@ -946,6 +1174,9 @@ msgstr "Створити новий каталог"
msgid "Create new file"
msgstr "Створити новий файл"
+msgid "Create new label"
+msgstr "Створити нову мітку"
+
msgid "Create new..."
msgstr "Створити..."
@@ -967,6 +1198,9 @@ msgstr "ЧаÑовий поÑÑ Cron"
msgid "Cron syntax"
msgstr "СинтакÑÐ¸Ñ Cron"
+msgid "Current node"
+msgstr "Поточний вузол"
+
msgid "Custom notification events"
msgstr "КориÑтувацькі Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ про події"
@@ -976,11 +1210,8 @@ msgstr "Спеціальні рівні Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÑпівпадÐ
msgid "Cycle Analytics"
msgstr "Ðналіз циклу"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr ""
-
msgid "CycleAnalyticsStage|Code"
-msgstr ""
+msgstr "ÐапиÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ"
msgid "CycleAnalyticsStage|Issue"
msgstr "Проблема"
@@ -989,7 +1220,7 @@ msgid "CycleAnalyticsStage|Plan"
msgstr "ПлануваннÑ"
msgid "CycleAnalyticsStage|Production"
-msgstr ""
+msgstr "Production"
msgid "CycleAnalyticsStage|Review"
msgstr "ЗатвердженнÑ"
@@ -1025,23 +1256,32 @@ msgstr[1] "РозгортаннÑ"
msgstr[2] "Розгортань"
msgid "Deploy Keys"
-msgstr ""
+msgstr "Ключі Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ"
msgid "Description"
msgstr "ОпиÑ"
msgid "Description templates allow you to define context-specific templates for issue and merge request description fields for your project."
-msgstr ""
+msgstr "Шаблони опиÑу дозволÑÑŽÑ‚ÑŒ визначити конкретні шаблони обговорень проблем та запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ проекту."
msgid "Details"
msgstr "Деталі"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "Ім'Ñ ÐºÐ°Ñ‚Ð°Ð»Ð¾Ð³Ñƒ"
+msgid "Disable"
+msgstr "Вимкнути"
+
msgid "Discard changes"
msgstr "СкаÑувати зміни"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Відмінити блок вÑтупу до Ðналитики Циклу"
@@ -1073,20 +1313,29 @@ msgid "DownloadCommit|Email Patches"
msgstr "Email-патчи"
msgid "DownloadCommit|Plain Diff"
-msgstr ""
+msgstr "ПроÑте порівнÑÐ½Ð½Ñ (diff)"
msgid "DownloadSource|Download"
msgstr "Завантажити"
+msgid "Due date"
+msgstr "Запланована дата завершеннÑ"
+
msgid "Edit"
msgstr "Редагувати"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Редагувати Розклад Конвеєра %{id}"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "ÐдреÑи електронної пошти"
+msgid "Enable"
+msgstr "Увімкнути"
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "Виникла помилка при завантаженні Ñередовищ."
@@ -1105,9 +1354,6 @@ msgstr "Середовище"
msgid "Environments|Environments"
msgstr "Середовища"
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr "ЗавданнÑ"
@@ -1150,9 +1396,33 @@ msgstr "Епіки дозволÑÑŽÑ‚ÑŒ керувати вашим портфе
msgid "Error creating epic"
msgstr "Помилка при Ñтворенні епіку"
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¼Ñ–Ñ‚Ð¾Ðº."
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñки на ÑповіщеннÑ"
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr "Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑтатуÑу Ð´Ð»Ñ Ð²ÑÑ–Ñ… задач."
+
+msgid "Error updating todo status."
+msgstr "Помилка при оновленні ÑтатуÑу задачі."
+
msgid "EventFilterBy|Filter by all"
msgstr "Фільтрувати по вÑім"
@@ -1166,7 +1436,7 @@ msgid "EventFilterBy|Filter by merge events"
msgstr "Фільтрувати по запитам на злиттÑ"
msgid "EventFilterBy|Filter by push events"
-msgstr ""
+msgstr "Фільтрувати за подіÑми Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ (push)"
msgid "EventFilterBy|Filter by team"
msgstr "Фільтрувати по команді"
@@ -1180,6 +1450,9 @@ msgstr "Кожен міÑÑць (1-го чиÑла о 4:00 ранку)"
msgid "Every week (Sundays at 4:00am)"
msgstr "Ð©Ð¾Ñ‚Ð¸Ð¶Ð½Ñ (в неділю о 4:00 ранку)"
+msgid "Expand"
+msgstr "Розгорнути"
+
msgid "Explore projects"
msgstr "ОглÑд проектів"
@@ -1190,7 +1463,7 @@ msgid "Failed to change the owner"
msgstr "Ðе вдалоÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ влаÑника"
msgid "Failed to remove the pipeline schedule"
-msgstr ""
+msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ розклад конвеєра"
msgid "Feb"
msgstr "лют."
@@ -1198,6 +1471,9 @@ msgstr "лют."
msgid "February"
msgstr "лютий"
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "Ім'Ñ Ñ„Ð°Ð¹Ð»Ñƒ"
@@ -1217,7 +1493,7 @@ msgid "FirstPushedBy|First"
msgstr "Перший"
msgid "FirstPushedBy|pushed by"
-msgstr ""
+msgstr "відправлено"
msgid "Fork"
msgid_plural "Forks"
@@ -1235,37 +1511,121 @@ msgid "Format"
msgstr "Формат"
msgid "From issue creation until deploy to production"
-msgstr ""
+msgstr "З моменту ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ до Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
msgid "From merge request merge until deploy to production"
-msgstr ""
+msgstr "Від Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð´Ð¾ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð° production"
msgid "GPG Keys"
msgstr "GPG ключі"
+msgid "Generate a default set of labels"
+msgstr ""
+
msgid "Geo Nodes"
msgstr "Гео-Вузли"
-msgid "GeoNodeSyncStatus|Failed"
-msgstr "Ðевдало"
-
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr "Вузол не працює або зламаний."
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr "Вузол працює повільно, перевантажений або тільки що відновивÑÑ Ð¿Ñ–ÑÐ»Ñ Ð·Ð±Ð¾ÑŽ."
-msgid "GeoNodeSyncStatus|Out of sync"
-msgstr "ÐеÑинхронізовано"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr "Ðевдало"
+
+msgid "GeoNodes|Full"
+msgstr "Повний"
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
-msgstr "Синхронізовано"
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
msgid "Geo|File sync capacity"
msgstr "ПропуÑкна здатніÑÑ‚ÑŒ Ñинхронізації файлів"
-msgid "Geo|Groups to replicate"
-msgstr "Групи Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ—"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
msgid "Geo|Repository sync capacity"
msgstr "ПропуÑкна здатніÑÑ‚ÑŒ Ñинхронізації репозиторіїв"
@@ -1273,12 +1633,24 @@ msgstr "ПропуÑкна здатніÑÑ‚ÑŒ Ñинхронізації репÐ
msgid "Geo|Select groups to replicate."
msgstr "Виберіть групи Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ—."
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ ÑÑ‚Ð°Ñ‚ÑƒÑ Ð·Ð±ÐµÑ€Ñ–Ð³Ð°Ð½Ð½Ñ Git була Ñкинута"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "Розділ GitLab Runner"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "Перейти до вашого форку"
@@ -1288,6 +1660,9 @@ msgstr "Форк"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "ÐÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Google не %{link_to_documentation}. ПопроÑÑ–Ñ‚ÑŒ Ñвого адмініÑтратора GitLab, Ñкщо ви хочете ÑкориÑтатиÑÑ Ñ†Ð¸Ð¼ ÑервіÑом."
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Заборонити Ñпільний доÑтуп до проекту в рамках %{group} з іншими групами"
@@ -1324,8 +1699,8 @@ msgstr "Групи не знайдені"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "Ви можете керувати правами доÑтупу членів групи мати доÑтуп до кожного проекту в ній."
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
-msgstr "Ви впевнені, що хочете залишити групу \"${this.group.fullName}\"?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
+msgstr ""
msgid "GroupsTree|Create a project in this group."
msgstr "Створити проект у групі."
@@ -1358,22 +1733,28 @@ msgid "Have your users email"
msgstr "Електронна пошта Ð´Ð»Ñ Ð·Ð²ÐµÑ€Ñ‚Ð°Ð½ÑŒ кориÑтувачів"
msgid "Health Check"
-msgstr "Перевірки працездатноÑÑ‚Ñ–"
+msgstr "Перевірка ПрацездатноÑÑ‚Ñ–"
msgid "Health information can be retrieved from the following endpoints. More information is available"
msgstr "Інформацію про працездатніÑÑ‚ÑŒ можна отримати з наÑтупних ендпойнтів. Більше інформації доÑтупно"
msgid "HealthCheck|Access token is"
-msgstr "Токен доÑтупу Ñ”"
+msgstr "Токен доÑтупу"
msgid "HealthCheck|Healthy"
msgstr "Здоровий"
msgid "HealthCheck|No Health Problems Detected"
-msgstr "Жодних проблем із здоров'Ñм не виÑвлено"
+msgstr "Проблем із здоров'Ñм не виÑвлено"
msgid "HealthCheck|Unhealthy"
-msgstr "Ðездорові"
+msgstr "Ðездоровий"
+
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
msgid "History"
msgstr "ІÑторіÑ"
@@ -1382,10 +1763,10 @@ msgid "Housekeeping successfully started"
msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато"
msgid "Import repository"
-msgstr ""
+msgstr "Імпорт репозиторію"
msgid "Improve Issue boards with GitLab Enterprise Edition."
-msgstr ""
+msgstr "Покращити дошки обговорень проблем за допомогою верÑÑ–Ñ— GitLab Enterprise Edition."
msgid "Improve issues management with Issue weight and GitLab Enterprise Edition."
msgstr "Покращити ÑƒÐ¿Ñ€Ð°Ð²Ð»Ñ–Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð°Ð¼Ð¸ з можливіÑÑ‚ÑŽ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð°Ð³Ð¸ проблеми за допомогою GitLab Enterprise Edition."
@@ -1402,6 +1783,12 @@ msgstr[0] "ІнÑтанÑ"
msgstr[1] "IнÑтанÑи"
msgstr[2] "ІнÑтанÑів"
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Ð’Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ â€” будь-Ñкий автентифікований кориÑтувач має доÑтуп до цієї групи та уÑÑ–Ñ… Ñ—Ñ— внутрішніх проектів."
@@ -1429,6 +1816,9 @@ msgstr "Дошки"
msgid "Issues"
msgstr "Проблеми"
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr "Ñіч."
@@ -1447,6 +1837,27 @@ msgstr "чер."
msgid "June"
msgstr "червень"
+msgid "Kubernetes"
+msgstr "Kubernetes"
+
+msgid "Kubernetes Cluster"
+msgstr "КлаÑтер Kubernetes"
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "Вимкнено"
@@ -1456,6 +1867,9 @@ msgstr "Увімкнено"
msgid "Labels"
msgstr "Мітки"
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "ОÑтанній %d день"
@@ -1481,11 +1895,14 @@ msgid "Last updated"
msgstr "ВоÑтаннє оновленно"
msgid "LastPushEvent|You pushed to"
-msgstr ""
+msgstr "Ви відправили зміни до"
msgid "LastPushEvent|at"
msgstr "в"
+msgid "Learn more"
+msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ"
+
msgid "Learn more in the"
msgstr "ДізнайтеÑÑŒ більше"
@@ -1504,15 +1921,18 @@ msgstr "Залишити проект"
msgid "License"
msgstr "ЛіцензіÑ"
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ %d події"
-msgstr[1] "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ %d подій"
-msgstr[2] "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ %d подій"
+msgid "Loading the GitLab IDE..."
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ IDE GitLab..."
msgid "Lock"
msgstr "БлокуваннÑ"
+msgid "Lock %{issuableDisplayName}"
+msgstr "Заблокувати %{issuableDisplayName}"
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "Заблоковано"
@@ -1522,12 +1942,21 @@ msgstr "Заблоковані файли"
msgid "Login"
msgstr "Вхід"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr "Керувати мітками"
+
msgid "Mar"
msgstr "бер."
msgid "March"
msgstr "березень"
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "МакÑимальна кількіÑÑ‚ÑŒ невдач в Ñховищі даних git"
@@ -1540,18 +1969,42 @@ msgstr "Медіана"
msgid "Members"
msgstr "КориÑтувачі"
+msgid "Merge Request"
+msgstr "Запит на злиттÑ"
+
msgid "Merge Requests"
msgstr "Запити на злиттÑ"
msgid "Merge events"
-msgstr ""
+msgstr "Події злиттÑ"
msgid "Merge request"
msgstr "Запит на злиттÑ"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "ПовідомленнÑ"
+msgid "Milestone"
+msgstr "Етап"
+
+msgid "Milestones|Delete milestone"
+msgstr "Видалити етап"
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr "Видалити етап %{milestoneTitle}?"
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ етап %{milestoneTitle}"
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr "Етап %{milestoneTitle} не знайдено"
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "не додаÑте SSH ключ"
@@ -1561,11 +2014,17 @@ msgstr "Моніторинг"
msgid "More information is available|here"
msgstr "тут"
+msgid "Move"
+msgstr "ПереміÑтити"
+
+msgid "Move issue"
+msgstr "ПереміÑтити проблему"
+
msgid "Multiple issue boards"
msgstr "Кілька дошок обговореннÑ"
-msgid "New Cluster"
-msgstr "Ðовий клаÑтер"
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
@@ -1573,6 +2032,12 @@ msgstr[0] "Ðова проблема"
msgstr[1] "Ðові проблеми"
msgstr[2] "Ðових проблем"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Ðовий розклад Конвеєра"
@@ -1597,6 +2062,9 @@ msgstr "Ðова група"
msgid "New issue"
msgstr "Ðова проблема"
+msgid "New label"
+msgstr "Ðова мітка"
+
msgid "New merge request"
msgstr "Ðовий запит на злиттÑ"
@@ -1607,7 +2075,7 @@ msgid "New schedule"
msgstr "Ðовий Розклад"
msgid "New snippet"
-msgstr ""
+msgstr "Ðовий Ñніпет"
msgid "New subgroup"
msgstr "Ðова підгрупа"
@@ -1615,12 +2083,27 @@ msgstr "Ðова підгрупа"
msgid "New tag"
msgstr "Ðовий тег"
-msgid "No container images stored for this project. Add one by following the instructions above."
-msgstr "Ð’ цьому проекті немає жодного образа контейнера. Додайте його за інÑтрукціÑми вище."
+msgid "No assignee"
+msgstr "Ðемає виконавцÑ"
-msgid "No repository"
+msgid "No changes"
+msgstr "Ðемає змін"
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr ""
+msgid "No due date"
+msgstr "Ðемає запланованої дати завершеннÑ"
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr "Файл не вибрано"
+
+msgid "No repository"
+msgstr "Ðемає репозиторію"
+
msgid "No schedules"
msgstr "немає Розкладів"
@@ -1630,9 +2113,15 @@ msgstr "Ðемає витраченого чаÑу"
msgid "None"
msgstr "Жоден"
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "ÐедоÑтупний"
+msgid "Not confidential"
+msgstr "Ðе конфіденційно"
+
msgid "Not enough data"
msgstr "ÐедоÑтатньо даних"
@@ -1643,13 +2132,13 @@ msgid "NotificationEvent|Close issue"
msgstr "Проблема закрита"
msgid "NotificationEvent|Close merge request"
-msgstr ""
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸Ð¹"
msgid "NotificationEvent|Failed pipeline"
msgstr "Ðевдача в конвеєрі"
msgid "NotificationEvent|Merge merge request"
-msgstr ""
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¾"
msgid "NotificationEvent|New issue"
msgstr "Ðова проблема"
@@ -1664,7 +2153,7 @@ msgid "NotificationEvent|Reassign issue"
msgstr "Перепризначити проблему"
msgid "NotificationEvent|Reassign merge request"
-msgstr ""
+msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿ÐµÑ€ÐµÐ¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¾"
msgid "NotificationEvent|Reopen issue"
msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ñƒ"
@@ -1693,6 +2182,12 @@ msgstr "ВідÑтежувати"
msgid "Notifications"
msgstr "СповіщеннÑ"
+msgid "Notifications off"
+msgstr "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð¾"
+
+msgid "Notifications on"
+msgstr "Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾"
+
msgid "Nov"
msgstr "лиÑÑ‚."
@@ -1702,8 +2197,8 @@ msgstr "лиÑтопад"
msgid "Number of access attempts"
msgstr "КількіÑÑ‚ÑŒ Ñпроб доÑтупу"
-msgid "Number of failures before backing off"
-msgstr "КількіÑÑ‚ÑŒ помилок до призупиненнÑ"
+msgid "OK"
+msgstr "OK"
msgid "Oct"
msgstr "жовт."
@@ -1717,6 +2212,9 @@ msgstr "Фільтр"
msgid "Only project members can comment."
msgstr "Тільки учаÑники проекту можуть залишати коментарі."
+msgid "Open"
+msgstr "Відкрити"
+
msgid "Opened"
msgstr "Відкрито"
@@ -1750,9 +2248,6 @@ msgstr "« Перша"
msgid "Password"
msgstr "Пароль"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "Люди без дозволу ніколи не отримуватимуть Ñповіщень Ñ– не зможуть коментувати."
-
msgid "Pipeline"
msgstr "Конвеєр"
@@ -1769,7 +2264,7 @@ msgid "Pipeline quota"
msgstr "Квота на конвеєри"
msgid "PipelineCharts|Failed:"
-msgstr "Ðе вдалоÑÑ:"
+msgstr "Ðевдалі:"
msgid "PipelineCharts|Overall statistics"
msgstr "Загальна ÑтатиÑтика"
@@ -1795,12 +2290,6 @@ msgstr "Ð’ÑÑ–"
msgid "PipelineSchedules|Inactive"
msgstr "Ðеактивні"
-msgid "PipelineSchedules|Input variable key"
-msgstr "Введіть ім'Ñ Ð·Ð¼Ñ–Ð½Ð½Ð¾Ñ—"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr ""
-
msgid "PipelineSchedules|Next Run"
msgstr "ÐаÑтупний запуÑк"
@@ -1808,10 +2297,7 @@ msgid "PipelineSchedules|None"
msgstr "Ðемає"
msgid "PipelineSchedules|Provide a short description for this pipeline"
-msgstr ""
-
-msgid "PipelineSchedules|Remove variable row"
-msgstr "Видалити змінні"
+msgstr "Задайте короткий Ð¾Ð¿Ð¸Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ конвеєру"
msgid "PipelineSchedules|Take ownership"
msgstr "Стати влаÑником"
@@ -1823,7 +2309,7 @@ msgid "PipelineSchedules|Variables"
msgstr "Змінні"
msgid "PipelineSheduleIntervalPattern|Custom"
-msgstr ""
+msgstr "Спеціальні"
msgid "Pipelines"
msgstr "Конвеєри"
@@ -1840,6 +2326,12 @@ msgstr "Конвеєри за оÑтанній тиждень"
msgid "Pipelines for last year"
msgstr "Конвеєри за оÑтанній рік"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "вÑÑ–"
@@ -1852,15 +2344,24 @@ msgstr "зі Ñтадією"
msgid "Pipeline|with stages"
msgstr "зі ÑтадіÑми"
+msgid "Play"
+msgstr "Відтворити"
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "Будь лаÑка, пройдіть reCAPTCHA"
msgid "Preferences"
msgstr "ÐалаштуваннÑ"
-msgid "Private - Project access must be granted explicitly to each user."
+msgid "Primary"
msgstr ""
+msgid "Private - Project access must be granted explicitly to each user."
+msgstr "Приватний — доÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві."
+
msgid "Private - The group and its projects can only be viewed by members."
msgstr "Приватна — цю групу та Ñ—Ñ— проекти можуть бачити тільки Ñ—Ñ— кориÑтувачі."
@@ -1903,6 +2404,9 @@ msgstr "Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” влаÑником в цих гÑ
msgid "Profiles|your account"
msgstr "ваш обліковий запиÑ"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Проект '%{project_name}' перебуває в процеÑÑ– видаленнÑ."
@@ -1916,8 +2420,17 @@ msgid "Project '%{project_name}' was successfully updated."
msgstr "Проект '%{project_name}' уÑпішно оновлено."
msgid "Project access must be granted explicitly to each user."
+msgstr "ДоÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві."
+
+msgid "Project avatar"
+msgstr "Ðватар проекту"
+
+msgid "Project avatar in repository: %{link}"
msgstr ""
+msgid "Project cache successfully reset."
+msgstr "Кеш проекту уÑпішно Ñкинуто."
+
msgid "Project details"
msgstr "Деталі проекту"
@@ -1936,11 +2449,26 @@ msgstr "Розпочато екÑпорт проекту. ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПідпиÑатиÑÑ"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr "Керівники"
+
+msgid "ProjectCreationLevel|No one"
+msgstr "Ðіхто"
+
msgid "ProjectFeature|Disabled"
msgstr "Вимкнено"
msgid "ProjectFeature|Everyone with access"
-msgstr ""
+msgstr "Ð’ÑÑ– із доÑтупом"
msgid "ProjectFeature|Only team members"
msgstr "Тільки члени команди"
@@ -1960,15 +2488,9 @@ msgstr "ІÑторіÑ"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора, щоб змінити це налаштуваннÑ."
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr "Зразу запуÑкати конвеєр у гілкці за замовчуваннÑм"
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "Тільки підпиÑані коміти можуть бути надіÑлані в цей репозиторій."
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr "Проблема Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð°Ð¼ÐµÑ‚Ñ€Ñ–Ð² CI / CD JavaScript"
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr "Цей параметр заÑтоÑовуєтьÑÑ Ð½Ð° рівні Ñервера та може бути перевизначений адмініÑтратором."
@@ -1979,7 +2501,7 @@ msgid "ProjectSettings|This setting will be applied to all projects unless overr
msgstr "Цей параметр буде заÑтоÑовано до вÑÑ–Ñ… проектів, Ñкщо адмініÑтратор не змінить його."
msgid "ProjectSettings|Users can only push commits to this repository that were committed with one of their own verified emails."
-msgstr ""
+msgstr "КориÑтувачі можуть відправлÑти в цей репозиторій лише Ñ‚Ñ– коміти, Ñкі міÑÑ‚ÑÑ‚ÑŒ одну із їхніх підтверджених Ð°Ð´Ñ€ÐµÑ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти."
msgid "Projects"
msgstr "Проекти"
@@ -2033,11 +2555,14 @@ msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example
msgstr "Базова адреÑа Prometheus API, наприклад http://prometheus.example.com/"
msgid "PrometheusService|Time-series monitoring service"
-msgstr "Моніторинг Prometheus"
+msgstr ""
msgid "PrometheusService|View environments"
msgstr "ПереглÑд Ñередовищ"
+msgid "Protip:"
+msgstr "Підказка:"
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Публічна — група та вÑÑ– публічні проекти можуть переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
@@ -2045,14 +2570,17 @@ msgid "Public - The project can be accessed without any authentication."
msgstr "Публічний — проект может переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
msgid "Push Rules"
-msgstr ""
+msgstr "Правила відправленнÑ"
msgid "Push events"
-msgstr ""
+msgstr "Події Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ (push)"
msgid "PushRule|Committer restriction"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ‚ÐµÑ€Ð°"
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "Докладніше"
@@ -2065,6 +2593,12 @@ msgstr "Гілки"
msgid "RefSwitcher|Tags"
msgstr "Теги"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr "ЗареєÑтруватиÑÑ / Увійти"
+
msgid "Registry"
msgstr "РеєÑÑ‚Ñ€"
@@ -2084,14 +2618,23 @@ msgid "Related Merge Requests"
msgstr "Пов'Ñзані запити на злиттÑ"
msgid "Related Merged Requests"
-msgstr ""
+msgstr "Пов'Ñзані виконані запити"
msgid "Remind later"
msgstr "Ðагадати пізніше"
+msgid "Remove"
+msgstr "Видалити"
+
+msgid "Remove avatar"
+msgstr "Видалити аватар"
+
msgid "Remove project"
msgstr "Видалити проект"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "Репозиторій"
@@ -2107,6 +2650,12 @@ msgstr "Оновити токен доÑтупу Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ прÐ
msgid "Reset runners registration token"
msgstr "Скинути реєÑтраційний токен runner-ів"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Revert this commit"
msgstr "Ðнулювати цей коміт"
@@ -2116,13 +2665,13 @@ msgstr "Ðнулювати цей запит на злиттÑ"
msgid "SSH Keys"
msgstr "Ключі SSH"
-msgid "Save"
-msgstr "Зберегти"
-
msgid "Save changes"
msgstr "Зберегти зміни"
msgid "Save pipeline schedule"
+msgstr "Зберегти розклад конвеєра"
+
+msgid "Save variables"
msgstr ""
msgid "Schedule a new pipeline"
@@ -2140,44 +2689,65 @@ msgstr "Тематичні дошки проблем"
msgid "Search branches and tags"
msgstr "Пошук гілок та тегів"
+msgid "Search milestones"
+msgstr "Пошук етапів"
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr "Пошук кориÑтувачів"
+
msgid "Seconds before reseting failure information"
msgstr "КількіÑÑ‚ÑŒ Ñекунд до ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про збої"
-msgid "Seconds to wait after a storage failure"
-msgstr "Скільки Ñекунд очікувати піÑÐ»Ñ Ð·Ð±Ð¾ÑŽ в Ñховищі даних"
-
msgid "Seconds to wait for a storage access attempt"
msgstr "КількіÑÑ‚ÑŒ Ñекунд Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ´ повторною Ñпробою доÑтупу до Ñховища даних"
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "Виберіть формат архіву"
msgid "Select a timezone"
msgstr "Вибрати чаÑовий поÑÑ"
+msgid "Select assignee"
+msgstr "Виберіть виконавцÑ"
+
+msgid "Select branch/tag"
+msgstr "Виберіть гілку або тег"
+
msgid "Select target branch"
msgstr "Вибір цільової гілки"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr "вер."
msgid "September"
msgstr "вереÑень"
+msgid "Server version"
+msgstr "ВерÑÑ–Ñ Ñервера"
+
msgid "Service Templates"
msgstr "Ð¡ÐµÑ€Ð²Ñ–Ñ ÑˆÐ°Ð±Ð»Ð¾Ð½Ñ–Ð²"
msgid "Set a password on your account to pull or push via %{protocol}."
-msgstr ""
+msgstr "Ð’Ñтановіть пароль Ð´Ð»Ñ Ñвого облікового запиÑу, щоб мати можливіÑÑ‚ÑŒ відправлÑти та отримувати через %{protocol}."
-msgid "Set up CI"
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI"
+msgid "Set up CI/CD"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
msgid "Set up Koding"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Koding"
msgid "Set up auto deploy"
-msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ðµ розгортаннÑ"
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾Ð³Ð¾ розгортаннÑ"
msgid "SetPasswordToCloneLink|set a password"
msgstr "вÑтановити пароль"
@@ -2185,6 +2755,15 @@ msgstr "вÑтановити пароль"
msgid "Settings"
msgstr "ÐалаштуваннÑ"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr "Показати батьківÑькі Ñторінки"
@@ -2200,9 +2779,6 @@ msgstr[2] "Показано %d подій"
msgid "Sidebar|Change weight"
msgstr "Змінити вагу"
-msgid "Sidebar|Edit"
-msgstr "Редагувати"
-
msgid "Sidebar|No"
msgstr "ÐÑ–"
@@ -2213,20 +2789,32 @@ msgid "Sidebar|Weight"
msgstr "Вага"
msgid "Snippets"
+msgstr "Сніпети"
+
+msgid "Something went wrong on our end"
msgstr ""
msgid "Something went wrong on our end."
msgstr "ЩоÑÑŒ пішло не так з нашого боку"
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr "ЩоÑÑŒ пішло не так, при Ñпробі зміни Ñтану Ð±Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ ${this.issuableDisplayName}"
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr "ЩоÑÑŒ пішло не так під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñ–Ð²"
msgid "Something went wrong while fetching the registry list."
msgstr "ЩоÑÑŒ пішло не так при отриманні ÑпиÑку із реєÑтру."
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "Сортувати за"
@@ -2342,10 +2930,10 @@ msgid "Specify the following URL during the Runner setup:"
msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:"
msgid "StarProject|Star"
-msgstr "ПідпиÑатиÑÑ"
+msgstr "В обрані"
msgid "Starred projects"
-msgstr "Відмічені проекти"
+msgstr "Обрані проекти"
msgid "Start a %{new_merge_request} with these changes"
msgstr "Почати %{new_merge_request} з цими змінами"
@@ -2356,17 +2944,17 @@ msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Runner!"
msgid "Stopped"
msgstr "Зупинено"
+msgid "Storage"
+msgstr ""
+
msgid "Subgroups"
msgstr "Підгрупи"
-msgid "Subscribe"
-msgstr "ПідпиÑатиÑÑ"
-
msgid "Switch branch/tag"
-msgstr ""
+msgstr "Перейти в гілку/тег"
msgid "System Hooks"
-msgstr ""
+msgstr "СиÑтемні гуки"
msgid "Tag"
msgid_plural "Tags"
@@ -2417,7 +3005,7 @@ msgid "TagsPage|Optionally, add a message to the tag."
msgstr "При бажанні Ви можете додати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð² тег."
msgid "TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page."
-msgstr ""
+msgstr "При бажанні, додайте Ð¾Ð¿Ð¸Ñ Ñ€ÐµÐ»Ñ–Ð·Ñƒ до тегу. Він буде збережений в базі даних GitLab Ñ– відображатиметьÑÑ Ð½Ð° Ñторінці тегів."
msgid "TagsPage|Release notes"
msgstr "ÐžÐ¿Ð¸Ñ Ñ€ÐµÐ»Ñ–Ð·Ñƒ"
@@ -2438,7 +3026,7 @@ msgid "TagsPage|This tag has no release notes."
msgstr "Цей тег не міÑтить опиÑу релізу."
msgid "TagsPage|Use git tag command to add a new one:"
-msgstr "ВикориÑтовуйте команду git tag, щоб додати новий:"
+msgstr "ВикориÑтовуйте команду git tag, щоб додати новий тег:"
msgid "TagsPage|Write your release notes or drag files here..."
msgstr "Ðапишіть Ñвій Ð¾Ð¿Ð¸Ñ Ñ€ÐµÐ»Ñ–Ð·Ñƒ або перетÑгніть файли Ñюди..."
@@ -2458,41 +3046,41 @@ msgstr "ДÑкую! Більше не показувати це повідомл
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "Розширений глобальний пошук в GitLab - це потужний інÑтрумент Ñкий заощаджує ваш чаÑ. ЗаміÑÑ‚ÑŒ Ð´ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ Ñ– витрати чаÑу, ви можете шукати код інших команд, Ñкий може допомогти у вашому проекті."
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
-msgstr "Поріг Ð¿Ñ€Ð¸Ð·ÑƒÐ¿Ð¸Ð½ÐµÐ½Ð½Ñ circuitbreaker має бути нижчий за поріг повного відключеннÑ"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
-msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
+msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"ÐапиÑÐ°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ\" показує Ñ‡Ð°Ñ Ð²Ñ–Ð´ першого коміту до ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ. Дані будуть автоматично додані піÑÐ»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ першого запиту на злиттÑ."
+
msgid "The collection of events added to the data gathered for that stage."
-msgstr ""
+msgstr "ÐšÐ¾Ð»ÐµÐºÑ†Ñ–Ñ Ð¿Ð¾Ð´Ñ–Ð¹ додана до даних, зібраних Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
msgid "The fork relationship has been removed."
-msgstr ""
+msgstr "Зв'Ñзок форку видалено."
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"Проблема\" показує, Ñкільки чаÑу потрібно від ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ до Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ—Ñ— до ÑкогоÑÑŒ етапу, або Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ на дошку. Почніть Ñтворювати проблеми, щоб переглÑдати дані Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
+
+msgid "The maximum file size allowed is 200KB."
msgstr ""
msgid "The number of attempts GitLab will make to access a storage."
msgstr "КількіÑÑ‚ÑŒ Ñпроб, Ñкі зробить GitLab Ð´Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу до Ñховища даних."
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
-msgstr "КількіÑÑ‚ÑŒ невдач, піÑÐ»Ñ Ñ‡Ð¾Ð³Ð¾ GitLab почне тимчаÑово блокувати доÑтуп до Ñховища на хоÑÑ‚Ñ–"
-
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "КількіÑÑ‚ÑŒ збоїв піÑÐ»Ñ Ñкої Gitlab повніÑÑ‚ÑŽ заблокує доÑтуп до Ñховища данних. Лічильник кількоÑÑ‚Ñ– збоїв може бути Ñкинутий в інтерфейÑÑ– адмініÑтратора (%{link_to_health_page}), або через %{api_documentation_link}."
msgid "The phase of the development lifecycle."
msgstr "Фаза життєвого циклу розробки."
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "Розклад конвеєрів запуÑкає в майбутньому конвеєри, Ð´Ð»Ñ Ð¿ÐµÐ²Ð½Ð¸Ñ… гілок або тегів. Заплановані конвеєри уÑпадковують Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð½Ð° доÑтуп до проекту на оÑнові пов'Ñзаного з ними кориÑтувача."
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
-msgstr ""
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"ПлануваннÑ\" відображаєтьÑÑ Ñ‡Ð°Ñ Ð²Ñ–Ð´ попереднього кроку до першого коміту. ДодаєтьÑÑ Ð°Ð²Ñ‚Ð¾Ð¼Ð°Ñ‚Ð¸Ñ‡Ð½Ð¾, Ñк тільки відправитьÑÑ Ð¿ÐµÑ€ÑˆÐ¸Ð¹ коміт."
msgid "The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle."
-msgstr ""
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"Production\" показує загальний Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ ÑтвореннÑм проблеми та розгортаннÑм коду у production. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ð½Ð¾Ñ— ідеї до production циклу."
msgid "The project can be accessed by any logged in user."
msgstr "ДоÑтуп до проекту можливий будь-Ñким зареєÑтрованим кориÑтувачем."
@@ -2504,13 +3092,13 @@ msgid "The repository for this project does not exist."
msgstr "Репозиторій Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту не Ñ–Ñнує."
msgid "The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request."
-msgstr ""
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"ЗатвердженнÑ\" показує Ñ‡Ð°Ñ Ð²Ñ–Ð´ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ про об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð´Ð¾ його виконаннÑ. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ запиту на злиттÑ."
msgid "The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time."
-msgstr ""
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"Staging\" показує Ñ‡Ð°Ñ Ð¼Ñ–Ð¶ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° розгортаннÑм коду у production. Дані автоматично додаютьÑÑ Ð¿Ñ–ÑÐ»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñƒ production вперше."
msgid "The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running."
-msgstr ""
+msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ \"ТеÑтуваннÑ\" показує чаÑ, Ñкий GitLab CI витрачає Ð´Ð»Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ конвеєра Ð´Ð»Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾Ð³Ð¾ запиту злиттÑ. Дані будуть автоматично додані піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÑˆÐ¾Ð³Ð¾ конвеєра."
msgid "The time in seconds GitLab will keep failure information. When no failures occur during this time, information about the mount is reset."
msgstr "КількіÑÑ‚ÑŒ Ñекунд, протÑгом Ñкої GitLab зберігає інформацію про збої. Якщо протÑгом цього періоду жодних збоїв не відбуваєтьÑÑ, Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ точку Ð¼Ð¾Ð½Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑкидаєтьÑÑ."
@@ -2518,20 +3106,47 @@ msgstr "КількіÑÑ‚ÑŒ Ñекунд, протÑгом Ñкої GitLab збе
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "КількіÑÑ‚ÑŒ Ñекунд, протÑгом Ñкої GitLab намагатиметьÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ доÑтуп до Ñховища даних. По завершенню цього періоду буде згенерована помилка про Ð¿ÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð»Ñ–Ð¼Ñ–Ñ‚Ñƒ чаÑу."
-msgid "The time taken by each data entry gathered by that stage."
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
msgstr ""
+msgid "The time taken by each data entry gathered by that stage."
+msgstr "ЧаÑ, витрачений на кожен елемент, зібраний на цій Ñтадії."
+
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3, 5, 9, Ñередніми 5, між 3, 5, 7, 8, Ñередніми (5 + 7) / 2 = 6."
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
+msgstr "Є проблеми з доÑтупом до Ñховища git: "
+
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
msgstr ""
msgid "This board\\'s scope is reduced"
msgstr "ВидиміÑÑ‚ÑŒ цієї дошки обмежена"
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
-msgstr "Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° була змінена піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾ моменту, коли ви почали Ñ—Ñ— редагувати. Ви хотіли б Ñтворити нову?"
+msgid "This directory"
+msgstr "Цей каталог"
msgid "This is a confidential issue."
msgstr "Це конфіденційна проблема."
@@ -2539,20 +3154,47 @@ msgstr "Це конфіденційна проблема."
msgid "This is the author's first Merge Request to this project."
msgstr "Це перший запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ–Ð´ цього автора Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту."
+msgid "This issue is confidential"
+msgstr "Ð¦Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð° Ñ” конфіденційною"
+
msgid "This issue is confidential and locked."
msgstr "Ð¦Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð° конфіденційна Ñ– заблокована."
msgid "This issue is locked."
msgstr "Ð¦Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð° заблокована."
-msgid "This means you can not push code until you create an empty repository or import existing one."
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
msgstr ""
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
+msgid "This means you can not push code until you create an empty repository or import existing one."
+msgstr "Це означає, що ви не можете відправлÑти код, поки не Ñтворите порожній репозиторій або не імпортуєте Ñ–Ñнуючий."
+
msgid "This merge request is locked."
msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾."
+msgid "This project"
+msgstr "Цей проект"
+
+msgid "This repository"
+msgstr "Цей репозиторій"
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
-msgstr ""
+msgstr "Ці Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти автоматично Ñтануть обговореннÑми проблем, Ñкі відображатимутьÑÑ Ñ‚ÑƒÑ‚ (причому коментарі Ñтануть чаÑтиною перепиÑки)."
msgid "Time before an issue gets scheduled"
msgstr "Ð§Ð°Ñ Ð´Ð¾ початку потраплÑÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð»ÐµÐ¼Ð¸ в планувальник"
@@ -2561,11 +3203,23 @@ msgid "Time before an issue starts implementation"
msgstr "Ð§Ð°Ñ Ð´Ð¾ початку роботи над проблемою"
msgid "Time between merge request creation and merge/close"
-msgstr ""
+msgstr "Ð§Ð°Ñ Ð¼Ñ–Ð¶ ÑтвореннÑм запиту Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– його виконаннÑм або закриттÑм"
+
+msgid "Time tracking"
+msgstr "ВідÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡Ð°Ñу"
msgid "Time until first merge request"
msgstr "Ð§Ð°Ñ Ð´Ð¾ першого запиту на злиттÑ"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr "%s днів тому"
@@ -2707,6 +3361,18 @@ msgstr "Ñекунд(а)"
msgid "Title"
msgstr "Ðазва"
+msgid "Todo"
+msgstr "Задача"
+
+msgid "Toggle sidebar"
+msgstr "Перемикач бічної панелі"
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ð°: ВИМКÐЕÐО"
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ð°: УВІМКÐЕÐО"
+
msgid "Total Time"
msgstr "Загальний чаÑ"
@@ -2717,40 +3383,61 @@ msgid "Total test time for all commits/merges"
msgstr "Загальний чаÑ, щоб перевірити вÑÑ– коміти/злиттÑ"
msgid "Track activity with Contribution Analytics."
-msgstr ""
+msgstr "ВідÑтежувати активніÑÑ‚ÑŒ за допомогою Ðналітики учаÑників."
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr "ВідÑтежуйте групи проблем зі Ñпільною темою з різних проектів та етапів"
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr "Ввімкнути Service Desk"
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr "Ðевідомо"
+
msgid "Unlock"
msgstr "Розблокувати"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "Розблоковано"
msgid "Unstar"
-msgstr "ВідпиÑатиÑÑŒ"
+msgstr "Видалити із обраних"
-msgid "Unsubscribe"
-msgstr "ВідпиÑатиÑÑ"
+msgid "Up to date"
+msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Перейдіть на вищий тарифний план щоб активувати Покращений Глобальний Пошук."
msgid "Upgrade your plan to activate Contribution Analytics."
-msgstr ""
+msgstr "Перейдіть на вищий тарифний план Ð´Ð»Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ— Ðналітики учаÑників."
msgid "Upgrade your plan to activate Group Webhooks."
-msgstr ""
+msgstr "Перейдіть на вищий тарифний план щоб активувати групові веб-гуки."
msgid "Upgrade your plan to activate Issue weight."
-msgstr ""
+msgstr "Перейдіть на вищий тарифний план щоб активувати вагу обговорень проблем."
msgid "Upgrade your plan to improve Issue boards."
-msgstr ""
+msgstr "Перейдіть на вищий тарифний план щоб покращити дошки обговорень."
msgid "Upload New File"
msgstr "Завантажити новий файл"
@@ -2758,6 +3445,9 @@ msgstr "Завантажити новий файл"
msgid "Upload file"
msgstr "Завантажити файл"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "ÐатиÑніть, щоб завантажити"
@@ -2770,9 +3460,15 @@ msgstr "ВикориÑтовувати токен під Ñ‡Ð°Ñ ÑƒÑтановк
msgid "Use your global notification setting"
msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr "ПереглÑд файла @ "
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "ПереглÑд відкритих запитів на злиттÑ"
@@ -2794,21 +3490,21 @@ msgstr "Ðевідомий"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "Хочете побачити дані? Будь лаÑка, попроÑить у адмініÑтратора доÑтуп."
-msgid "We don't have enough data to show this stage."
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
msgstr ""
+msgid "We don't have enough data to show this stage."
+msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
+
msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr "Ми хочемо бути впевнені, що це ви, будь лаÑка, підтвердіть, що ви не робот."
msgid "Webhooks allow you to trigger a URL if, for example, new code is pushed or a new issue is created. You can configure webhooks to listen for specific events like pushes, issues or merge requests. Group webhooks will apply to all projects in a group, allowing you to standardize webhook functionality across your entire group."
-msgstr ""
+msgstr "Веб-гук дозволÑÑ” вам викликати URL Ñкщо, наприклад, був відправлений новий код або Ñтворено нову проблему. Ви можете налаштувати його так, щоб він реагував на певні події (відправки коду, проблеми або запити на злиттÑ). Групові веб-гуки заÑтоÑовуютьÑÑ Ð´Ð¾ вÑÑ–Ñ… проектів в групі Ñ– дозволÑÑŽÑ‚ÑŒ вам Ñтандартизувати Ñ—Ñ… Ð´Ð»Ñ Ð²Ñієї вашої групи."
msgid "Weight"
msgstr "Вага"
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr "Коли відбуваєтьÑÑ Ð·Ð±Ñ–Ð¹ при доÑтупі до Ñховища даних, GitLab блокує доÑуп до нього протÑгом періоду чаÑу, заданому тут. Це дає можливіÑÑ‚ÑŒ файловій ÑиÑтемі відновитиÑÑ. Репозиторії на шардах (shards) зі збоÑми тимчаÑово не доÑтупні"
-
msgid "Wiki"
msgstr "Wiki"
@@ -2827,9 +3523,15 @@ msgstr "РекомендуєтьÑÑ Ð²Ñтановити %{markdown}, з тим
msgid "WikiClone|Start Gollum and edit locally"
msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Gollum Ñ– редагуйте локально"
-msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
msgstr ""
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
+msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
+msgstr "Ви не можете Ñтворювати wiki-Ñторінки"
+
msgid "WikiHistoricalPage|This is an old version of this page."
msgstr "Це — Ñтара верÑÑ–Ñ Ñторінки."
@@ -2846,7 +3548,7 @@ msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
msgstr "Більше прикладів знаходитьÑÑ Ð² %{docs_link}"
msgid "WikiMarkdownDocs|documentation"
-msgstr "документаціÑ"
+msgstr "документації"
msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
msgstr "Ð”Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾ÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° (нову) Ñторінку, проÑто введіть %{link_example}"
@@ -2858,7 +3560,7 @@ msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We wi
msgstr "Порада: можна вказати повний шлÑÑ… до нового файлу. Ми автоматично Ñтворимо вÑÑ– відÑутні каталоги."
msgid "WikiNewPageTitle|New Wiki Page"
-msgstr ""
+msgstr "Ðова wiki-Ñторінка"
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
msgstr "Ви дійÑно бажаєте видалити цю Ñторінку?"
@@ -2909,10 +3611,10 @@ msgid "Wiki|Pages"
msgstr "Сторінки"
msgid "Wiki|Wiki Pages"
-msgstr ""
+msgstr "Wiki-Ñторінки"
msgid "With contribution analytics you can have an overview for the activity of issues, merge requests and push events of your organization and its members."
-msgstr ""
+msgstr "З аналітикою учаÑників ви може вивчати активніÑÑ‚ÑŒ в обговореннÑÑ…, запитах на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– змінах у коді Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— організації та Ñ—Ñ— учаÑників."
msgid "Withdraw Access Request"
msgstr "СкаÑувати запит доÑтупу"
@@ -2929,7 +3631,19 @@ msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ зв'Ñзок з форка Ð
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "Ви збираєтеÑÑ Ð¿ÐµÑ€ÐµÐ´Ð°Ñ‚Ð¸ проект %{project_name_with_namespace} іншому влаÑнику. Ви ÐБСОЛЮТÐО впевнені?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
+msgstr "Ви можете додавати файли тільки коли перебуваєте в гілці"
+
+msgid "You can only edit files when you are on a branch"
msgstr ""
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
@@ -2963,12 +3677,18 @@ msgid "You will receive notifications only for comments in which you were @menti
msgstr "Ви будете отримувати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ Ð´Ð»Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ–Ð², в Ñких ви були @згадані"
msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
-msgstr ""
+msgstr "Ви не зможете відправлÑти та отримувати код проекту через %{protocol} поки не %{set_password_link} Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ облікового запиÑу"
msgid "You won't be able to pull or push project code via SSH until you %{add_ssh_key_link} to your profile"
-msgstr ""
+msgstr "Ви не зможете відправлÑти та отримувати код проекту через SSH поки не %{add_ssh_key_link} до вашого профілю"
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
+msgstr "Ви не зможете відправлÑти та отримувати код проекту через SSH, поки не додаÑте в Ñвій профіль SSH ключ"
+
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
msgstr ""
msgid "Your comment will not be visible to the public."
@@ -2983,32 +3703,227 @@ msgstr "Ваше ім'Ñ"
msgid "Your projects"
msgstr "Ваші проекти"
+msgid "assign yourself"
+msgstr "призначити Ñебе"
+
msgid "branch name"
msgstr "ім'Ñ Ð³Ñ–Ð»ÐºÐ¸"
msgid "by"
msgstr "від"
+msgid "ciReport|Code quality"
+msgstr "ЯкіÑÑ‚ÑŒ коду"
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð·Ð²Ñ–Ñ‚Ñƒ ${type} пройшло невдало"
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr "ІнÑтанÑи"
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr "SAST"
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr "коміт"
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "день"
msgstr[1] "дні"
msgstr[2] "днів"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] "запит на злиттÑ"
+msgstr[1] "запити на злиттÑ"
+msgstr[2] "запитів на злиттÑ"
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr "СкаÑувати автоматичне злиттÑ"
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr "ЗлиттÑ"
+
+msgid "mrWidget|Merge failed."
+msgstr "Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾Ð¹ÑˆÐ»Ð¾ невдало."
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr "Оновити"
+
+msgid "mrWidget|Refresh now"
+msgstr "Оновити зараз"
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr "ВідбулаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при автоматичному злитті цього запиту"
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "Ðовий запит на злиттÑ"
msgid "notification emails"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою"
+msgid "or"
+msgstr "або"
+
msgid "parent"
msgid_plural "parents"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
+msgstr[0] "батьківÑький об’єкт"
+msgstr[1] "батьківÑькі об’єкти"
+msgstr[2] "батьківÑький об’єктів"
msgid "password"
msgstr "пароль"
@@ -3016,12 +3931,21 @@ msgstr "пароль"
msgid "personal access token"
msgstr "оÑобиÑтий токен доÑтупу"
+msgid "remove due date"
+msgstr "видалити заплановану дату завершеннÑ"
+
msgid "source"
msgstr "джерело"
-msgid "to help your contributors communicate effectively!"
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr ""
+msgid "to help your contributors communicate effectively!"
+msgstr "щоб допомогти учаÑникам ефективно ÑпілкуватиÑÑ!"
+
msgid "username"
msgstr "ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 9c0d8dd5ddc..441f080596c 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:58-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Simplified\n"
"Language: zh_CN\n"
@@ -16,20 +16,35 @@ msgstr ""
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d 次æ交"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d 层"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "为æ高页é¢åŠ è½½é€Ÿåº¦åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。"
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "ç”± %{commit_author_link} æ交于 %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -41,9 +56,6 @@ msgstr "%{number_commits_behind} 个è½åŽ %{default_branch} 分支的æ交, %{
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "已失败 %{number_of_failures} 次/最多å…许失败失败 %{maximum_failures} 次,GitLab 将继续é‡è¯•ã€‚"
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "已失败 %{number_of_failures} 次/最多å…许失败 %{maximum_failures} 次,GitLab 将在 %{number_of_seconds} 秒åŽé‡è¯•ã€‚"
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "已失败 %{number_of_failures} 次/最多å…许失败 %{maximum_failures} 次,GitLab ä¸ä¼šç»§ç»­è‡ªåŠ¨é‡è¯•ã€‚请在问题解决åŽé‡ç½®å­˜å‚¨å¥åº·ä¿¡æ¯ã€‚"
@@ -115,24 +127,81 @@ msgstr "添加许å¯è¯"
msgid "Add new directory"
msgstr "添加目录"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "å¥åº·é¡µé¢"
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr "高级设置"
msgid "All"
msgstr "全部"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr "切æ¢é€šçŸ¥è®¢é˜…æ—¶å‘生错误"
msgid "An error occurred when updating the issue weight"
msgstr "更新议题æƒé‡æ—¶å‘生错误"
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr "获å–侧边æ æ•°æ®æ—¶å‘生错误"
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr "å‘生了错误,请å†è¯•ä¸€æ¬¡ã€‚"
@@ -157,9 +226,6 @@ msgstr "确定è¦åˆ é™¤æ­¤æµæ°´çº¿è®¡åˆ’å—?"
msgid "Are you sure you want to discard your changes?"
msgstr "确定è¦æ”¾å¼ƒä¿®æ”¹å—?"
-msgid "Are you sure you want to leave this group?"
-msgstr "确定è¦ç¦»å¼€è¿™ä¸ªç¾¤ç»„å—?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "确定è¦é‡ç½®æ³¨å†Œä»¤ç‰Œå—?"
@@ -172,6 +238,21 @@ msgstr "确定å—?"
msgid "Artifacts"
msgstr "产物"
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放文件到此处或者 %{upload_link}"
@@ -187,15 +268,18 @@ msgstr "认è¯æ—¥å¿—"
msgid "Author"
msgstr "作者"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "自动审查程åºå’Œè‡ªåŠ¨éƒ¨ç½²ç¨‹åºéœ€è¦ä¸€ä¸ªåŸŸåå’Œ %{kubernetes} æ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "自动审查程åºå’Œè‡ªåŠ¨éƒ¨ç½²ç¨‹åºéœ€è¦ä¸€ä¸ªåŸŸåæ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "自动审查程åºå’Œè‡ªåŠ¨éƒ¨ç½²ç¨‹åºéœ€è¦ %{kubernetes} æ‰èƒ½æ­£å¸¸å·¥ä½œã€‚"
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "DevOps 自动化(测试版)"
@@ -217,6 +301,12 @@ msgstr "您å¯ä»¥ä¸ºæ­¤é¡¹ç›®æ¿€æ´» %{link_to_settings}。"
msgid "Available"
msgstr "å¯ç”¨çš„"
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr "è´¦å•"
@@ -271,6 +361,9 @@ msgstr "æ¯å¹´æ”¯ä»˜ %{price_per_year}"
msgid "BillingPlans|per user"
msgstr "æ¯ç”¨æˆ·"
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "分支"
@@ -398,8 +491,8 @@ msgstr "作者:"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "CI é…ç½®"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr "作业"
@@ -410,6 +503,9 @@ msgstr "å–消"
msgid "Cancel edit"
msgstr "å–消编辑"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr "改å˜æƒé‡"
@@ -425,15 +521,24 @@ msgstr "优选"
msgid "ChangeTypeAction|Revert"
msgstr "还原"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "更新日志"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "统计图"
msgid "Chat"
msgstr "å³æ—¶é€šè®¯"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr "正在检查%{text}çš„å¯ç”¨æ€§..."
@@ -446,8 +551,20 @@ msgstr "优选此æ交"
msgid "Cherry-pick this merge request"
msgstr "优选此åˆå¹¶è¯·æ±‚"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
-msgstr "选择è¦å¤åˆ¶åˆ°æ­¤èŠ‚点的群组。留空则å¤åˆ¶æ‰€æœ‰ã€‚"
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
+msgstr ""
msgid "CiStatusLabel|canceled"
msgstr "å·²å–消"
@@ -503,80 +620,92 @@ msgstr "已跳过"
msgid "CiStatus|running"
msgstr "è¿è¡Œä¸­"
+msgid "CiVariables|Input variable key"
+msgstr ""
+
+msgid "CiVariables|Input variable value"
+msgstr ""
+
+msgid "CiVariables|Remove variable row"
+msgstr ""
+
+msgid "CiVariable|* (All environments)"
+msgstr ""
+
+msgid "CiVariable|All environments"
+msgstr ""
+
+msgid "CiVariable|Create wildcard"
+msgstr ""
+
+msgid "CiVariable|Error occured while saving variables"
+msgstr ""
+
+msgid "CiVariable|New environment"
+msgstr ""
+
+msgid "CiVariable|Protected"
+msgstr ""
+
+msgid "CiVariable|Search environments"
+msgstr ""
+
+msgid "CiVariable|Toggle protected"
+msgstr ""
+
+msgid "CiVariable|Validation failed"
+msgstr ""
+
msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr "断路器 API"
+msgid "Click to expand text"
+msgstr ""
+
msgid "Clone repository"
msgstr "克隆存储库"
msgid "Close"
msgstr "关闭"
-msgid "Cluster"
-msgstr "集群"
-
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
-msgstr "%{appList}å·²æˆåŠŸå®‰è£…在您的群集上"
+msgid "Closed"
+msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
-msgstr "%{boldNotice}这会增加一些é¢å¤–的资æºï¼Œå¦‚è´Ÿè½½å‡è¡¡å™¨ï¼Œè¿™ä¼šäº§ç”Ÿé¢å¤–çš„æˆæœ¬ã€‚请å‚阅%{pricingLink}"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|API URL"
msgstr "API地å€"
-msgid "ClusterIntegration|Active"
-msgstr "å¯ç”¨"
-
-msgid "ClusterIntegration|Add an existing cluster"
-msgstr "添加一个现有的集群"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Add cluster"
-msgstr "添加集群"
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|All"
-msgstr "所有"
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
msgid "ClusterIntegration|Applications"
msgstr "应用程åº"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
+
msgid "ClusterIntegration|CA Certificate"
msgstr "CAè¯ä¹¦"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "è¯ä¹¦æŽˆæƒåŒ…(PEMæ ¼å¼)"
-msgid "ClusterIntegration|Choose how to set up cluster integration"
-msgstr "选择如何设置集群集æˆ"
-
-msgid "ClusterIntegration|Cluster"
-msgstr "集群"
-
-msgid "ClusterIntegration|Cluster details"
-msgstr "集群详情"
-
-msgid "ClusterIntegration|Cluster integration"
-msgstr "集群集æˆ"
-
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "此项目已ç¦ç”¨é›†ç¾¤é›†æˆã€‚"
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "此项目已å¯ç”¨é›†ç¾¤é›†æˆã€‚"
-
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "此项目已å¯ç”¨é›†ç¾¤é›†æˆã€‚ç¦ç”¨æ­¤é›†æˆä¸ä¼šå½±å“您的集群,它åªä¼šæš‚时关闭 GitLab 的连接。"
-
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
-msgstr "群集正在Google Kubernetes Engine上创建..."
-
-msgid "ClusterIntegration|Cluster name"
-msgstr "集群å称"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
-msgstr "集群已在Google Kubernetes Engine上æˆåŠŸåˆ›å»ºã€‚刷新页é¢ä»¥æŸ¥çœ‹é›†ç¾¤çš„详细信æ¯"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
-msgstr "集群å…许您使用审阅应用程åºã€éƒ¨ç½²åº”用程åºã€è¿è¡Œæµæ°´çº¿ç­‰ç­‰ã€‚%{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
+msgstr ""
msgid "ClusterIntegration|Copy API URL"
msgstr "å¤åˆ¶API地å€"
@@ -584,38 +713,35 @@ msgstr "å¤åˆ¶API地å€"
msgid "ClusterIntegration|Copy CA Certificate"
msgstr "å¤åˆ¶CAè¯ä¹¦"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
+msgstr ""
+
msgid "ClusterIntegration|Copy Token"
msgstr "å¤åˆ¶ä»¤ç‰Œ"
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "å¤åˆ¶é›†ç¾¤å称"
-
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
-msgstr "在 GitLab 上创建一个 Google Engine 集群"
+msgid "ClusterIntegration|Create Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Create cluster"
-msgstr "创建集群"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
-msgstr "在 Google Kubernetes Engine 上创建集群"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
+msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr "在GKE中创建"
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "å¯ç”¨é›†ç¾¤é›†æˆ"
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr "输入现有的 Kubernetes 集群详细信æ¯"
-msgid "ClusterIntegration|Enter the details for your cluster"
-msgstr "输入您的集群详细信æ¯"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Environment pattern"
-msgstr "环境模å¼"
+msgid "ClusterIntegration|Environment scope"
+msgstr ""
-msgid "ClusterIntegration|GKE pricing"
-msgstr "GKEä»·æ ¼"
+msgid "ClusterIntegration|GitLab Integration"
+msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr "GitLab Runner"
@@ -632,47 +758,83 @@ msgstr "Google Kubernetes Engine 项目"
msgid "ClusterIntegration|Helm Tiller"
msgstr "Helm Tiller"
-msgid "ClusterIntegration|Inactive"
-msgstr "待用"
-
msgid "ClusterIntegration|Ingress"
msgstr "å…¥å£"
msgid "ClusterIntegration|Install"
msgstr "安装"
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr "在集群上安装应用程åºã€‚阅读更多关于%{helpLink}"
-
msgid "ClusterIntegration|Installed"
msgstr "已安装"
msgid "ClusterIntegration|Installing"
msgstr "安装中"
-msgid "ClusterIntegration|Integrate cluster automation"
-msgstr "集群自动化集æˆ"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
+msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "了解详细%{link_to_documentation}"
-msgid "ClusterIntegration|Learn more about Clusters"
-msgstr "了解更多集群的信æ¯"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
+msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr "机器类型"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "ç¡®ä¿æ‚¨çš„å¸æˆ·ç¬¦åˆåˆ›å»ºé›†ç¾¤çš„%{link_to_requirements}"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
-msgstr "在 GitLab 项目上管ç†é›†ç¾¤é›†æˆ"
+msgid "ClusterIntegration|Manage"
+msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "访问%{link_gke}æ¥ç®¡ç†æ‚¨çš„集群"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
-msgstr "GitLabä¼ä¸šé«˜çº§ç‰ˆå’Œæ——舰版æ供了多个集群"
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
+msgstr ""
msgid "ClusterIntegration|Note:"
msgstr "注æ„:"
@@ -680,18 +842,12 @@ msgstr "注æ„:"
msgid "ClusterIntegration|Number of nodes"
msgstr "节点数é‡"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
-msgstr "请为您的群集输入访问信æ¯ã€‚如果您需è¦å¸®åŠ©ï¼Œå¯ä»¥é˜…读我们关于集群的 %{link_to_help_page}"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
+msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "请确ä¿æ‚¨çš„ Google å¸æˆ·ç¬¦åˆä»¥ä¸‹è¦æ±‚:"
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr "设置集群时出现问题"
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr "设置集群列表时出现问题"
-
msgid "ClusterIntegration|Project ID"
msgstr "项目 ID"
@@ -701,17 +857,20 @@ msgstr "项目命å空间"
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "项目命å空间(å¯é€‰ï¼Œå”¯ä¸€)"
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
-msgstr "请阅读关于集群集æˆçš„%{link_to_help_page}。"
+msgid "ClusterIntegration|Prometheus"
+msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
-msgstr "删除集群集æˆ"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr "删除集æˆ"
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
-msgstr "删除集群集æˆå°†åˆ é™¤å·²æ·»åŠ åˆ°æ­¤é¡¹ç›®çš„集群é…置。它ä¸ä¼šåˆ é™¤ Google Kubernetes Engine 上的集群。"
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
+msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "请求安装失败"
@@ -719,8 +878,8 @@ msgstr "请求安装失败"
msgid "ClusterIntegration|Save changes"
msgstr "ä¿å­˜æ›´æ”¹"
-msgid "ClusterIntegration|See and edit the details for your cluster"
-msgstr "查看并编辑集群的详细信æ¯"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|See machine types"
msgstr "å‚è§æœºå™¨ç±»åž‹"
@@ -740,26 +899,26 @@ msgstr "显示"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "å‘生了内部错误"
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
-msgstr "在 Google Kubernetes Engine 上创建集群时å‘生错误"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "安装 %{title} æ—¶å‘生故障"
-msgid "ClusterIntegration|There are no clusters to show"
-msgstr "没有è¦æ˜¾ç¤ºçš„集群"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
+msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
-msgstr "æ­¤å¸æˆ·å¿…须有æƒåœ¨ä¸‹é¢æŒ‡å®šçš„%{link_to_container_project}中创建集群"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
+msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
-msgstr "切æ¢é›†ç¾¤"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|Token"
msgstr "令牌"
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "使用与此项目关è”的集群,您å¯ä»¥ä½¿ç”¨å®¡é˜…应用程åºï¼Œéƒ¨ç½²åº”用程åºï¼Œè¿è¡Œæµæ°´çº¿ç­‰ç­‰ã€‚"
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "您的å¸æˆ·å¿…须拥有%{link_to_kubernetes_engine}"
@@ -770,8 +929,8 @@ msgstr "区域"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr "访问 Google Kubernetes Engine"
-msgid "ClusterIntegration|cluster"
-msgstr "集群"
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
msgid "ClusterIntegration|documentation"
msgstr "文档"
@@ -788,6 +947,9 @@ msgstr "符åˆè¦æ±‚"
msgid "ClusterIntegration|properly configured"
msgstr "正确é…ç½®"
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "评论"
@@ -804,6 +966,9 @@ msgstr "最近30次æ交相应æŒç»­é›†æˆèŠ±è´¹çš„时间(分钟)"
msgid "Commit message"
msgstr "æ交信æ¯"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "æ交"
@@ -816,15 +981,57 @@ msgstr "æ交"
msgid "Commits feed"
msgstr "æ交动æ€"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "历å²"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "æ交者:"
msgid "Compare"
msgstr "比较"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr "容器注册"
@@ -876,6 +1083,9 @@ msgstr "贡献指å—"
msgid "Contributors"
msgstr "贡献者"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr "构建存储库图标。"
@@ -897,9 +1107,18 @@ msgstr "å¤åˆ¶ SSH 公钥到剪贴æ¿"
msgid "Copy URL to clipboard"
msgstr "å¤åˆ¶ URL 到剪贴æ¿"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "å¤åˆ¶æ交 SHA 的值到剪贴æ¿"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "创建新目录"
@@ -918,6 +1137,9 @@ msgstr "创建EPIC"
msgid "Create file"
msgstr "创建文件"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "创建åˆå¹¶è¯·æ±‚"
@@ -930,6 +1152,9 @@ msgstr "创建新目录"
msgid "Create new file"
msgstr "创建新文件"
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "创建..."
@@ -951,6 +1176,9 @@ msgstr "Cron 时区"
msgid "Cron syntax"
msgstr "Cron 语法"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "自定义通知事件"
@@ -960,9 +1188,6 @@ msgstr "自定义通知级别继承自å‚与级别。使用自定义通知级别
msgid "Cycle Analytics"
msgstr "周期分æž"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "周期分æžæ¦‚述了项目从想法到产å“实现的å„阶段所需的时间。"
-
msgid "CycleAnalyticsStage|Code"
msgstr "ç¼–ç "
@@ -1018,12 +1243,21 @@ msgstr "æ述模æ¿å…许您为项目的问题和åˆå¹¶è¯·æ±‚定义æ述字段
msgid "Details"
msgstr "详情"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "目录å称"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "放弃更改"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "关闭循环分æžä»‹ç»æ¡†"
@@ -1060,15 +1294,24 @@ msgstr "差异文件"
msgid "DownloadSource|Download"
msgstr "下载"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "编辑"
msgid "Edit Pipeline Schedule %{id}"
msgstr "编辑 %{id} æµæ°´çº¿è®¡åˆ’"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "电å­é‚®ä»¶"
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr "获å–环境时å‘生错误。"
@@ -1087,9 +1330,6 @@ msgstr "环境"
msgid "Environments|Environments"
msgstr "环境"
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr "环境是部署代ç çš„地方,例如预生产或生产。"
-
msgid "Environments|Job"
msgstr "作业"
@@ -1132,9 +1372,33 @@ msgstr "EPIC让你更有效率地管ç†ä½ çš„项目组åˆï¼Œè€Œä¸”ä¸è´¹å¹ç°ä¹
msgid "Error creating epic"
msgstr "创建EPIC时出错"
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr "切æ¢é€šçŸ¥è®¢é˜…æ—¶å‘生错误"
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "全部"
@@ -1162,6 +1426,9 @@ msgstr "æ¯æœˆæ‰§è¡Œï¼ˆæ¯æœˆ 1 日凌晨 4 点)"
msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯å‘¨æ‰§è¡Œï¼ˆå‘¨æ—¥å‡Œæ™¨ 4 点)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr "查看项目"
@@ -1180,6 +1447,9 @@ msgstr "二"
msgid "February"
msgstr "二月"
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "文件å"
@@ -1223,29 +1493,113 @@ msgstr "从åˆå¹¶è¯·æ±‚被åˆå¹¶åŽåˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒ"
msgid "GPG Keys"
msgstr "GPG 密钥"
+msgid "Generate a default set of labels"
+msgstr ""
+
msgid "Geo Nodes"
msgstr "Geo 节点"
-msgid "GeoNodeSyncStatus|Failed"
-msgstr "失败"
-
msgid "GeoNodeSyncStatus|Node is failing or broken."
msgstr "节点出现故障或æŸå。"
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr "节点è¿è¡Œç¼“æ…¢ã€è¶…è½½, 或者在åœæœºåŽåˆšåˆšæ¢å¤ã€‚"
-msgid "GeoNodeSyncStatus|Out of sync"
-msgstr "未åŒæ­¥"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
-msgstr "å·²åŒæ­¥"
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
+msgstr ""
msgid "Geo|File sync capacity"
msgstr "文件åŒæ­¥é‡"
-msgid "Geo|Groups to replicate"
-msgstr "å¤åˆ¶ç¾¤ç»„"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
+msgstr ""
msgid "Geo|Repository sync capacity"
msgstr "存储库åŒæ­¥é‡"
@@ -1253,12 +1607,24 @@ msgstr "存储库åŒæ­¥é‡"
msgid "Geo|Select groups to replicate."
msgstr "选择è¦å¤åˆ¶çš„群组。"
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Git 存储å¥åº·ä¿¡æ¯å·²é‡ç½®"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "GitLab Runner"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "跳转到派生项目"
@@ -1268,6 +1634,9 @@ msgstr "跳转到派生项目"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Google 身份验è¯ä¸æ˜¯%{link_to_documentation}。如果您想使用此æœåŠ¡ï¼Œè¯·å’¨è¯¢æ‚¨çš„ GitLab 管ç†å‘˜ã€‚"
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "ç¦æ­¢ä¸Žå…¶ä»–群组共享 %{group} 中的项目"
@@ -1304,8 +1673,8 @@ msgstr "找ä¸åˆ°ç¾¤ç»„"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "您å¯ä»¥ç®¡ç†ç¾¤ç»„æˆå‘˜çš„æƒé™å¹¶è®¿é—®ç¾¤ç»„中的æ¯ä¸ªé¡¹ç›®ã€‚"
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
-msgstr "您确定è¦ç¦»å¼€ç¾¤ç»„“${this.group.fullName}â€å—?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
+msgstr ""
msgid "GroupsTree|Create a project in this group."
msgstr "在此群组中创建一个项目。"
@@ -1355,6 +1724,10 @@ msgstr "没有检测到å¥åº·é—®é¢˜"
msgid "HealthCheck|Unhealthy"
msgstr "éžå¥åº·"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+
msgid "History"
msgstr "历å²"
@@ -1380,6 +1753,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] "例å­"
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "内部 - 任何登录的用户都å¯ä»¥æŸ¥çœ‹è¯¥ç¾¤ç»„和任何内部项目。"
@@ -1407,6 +1786,9 @@ msgstr "看æ¿"
msgid "Issues"
msgstr "议题"
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr "一"
@@ -1425,6 +1807,27 @@ msgstr "å…­"
msgid "June"
msgstr "六月"
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "åœç”¨"
@@ -1434,6 +1837,9 @@ msgstr "å¯ç”¨"
msgid "Labels"
msgstr "标签"
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
@@ -1462,6 +1868,9 @@ msgstr "您推é€äº†"
msgid "LastPushEvent|at"
msgstr "于"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "了解更多"
@@ -1480,13 +1889,18 @@ msgstr "退出项目"
msgid "License"
msgstr "许å¯åè®®"
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "最多显示 %d 个事件"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr "é”定"
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "å·²é”定"
@@ -1496,12 +1910,21 @@ msgstr "å·²é”定文件"
msgid "Login"
msgstr "登录"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr "三"
msgid "March"
msgstr "三月"
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "最大 git 存储失败"
@@ -1514,6 +1937,9 @@ msgstr "中ä½æ•°"
msgid "Members"
msgstr "æˆå‘˜"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr "åˆå¹¶è¯·æ±‚"
@@ -1523,9 +1949,30 @@ msgstr "åˆå¹¶äº‹ä»¶"
msgid "Merge request"
msgstr "åˆå¹¶è¯·æ±‚"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "消æ¯"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "新建 SSH 公钥"
@@ -1535,16 +1982,28 @@ msgstr "监控"
msgid "More information is available|here"
msgstr "帮助文档"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr "多个议题看æ¿"
-msgid "New Cluster"
-msgstr "新集群"
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "新建议题"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "创建æµæ°´çº¿è®¡åˆ’"
@@ -1569,6 +2028,9 @@ msgstr "新群组"
msgid "New issue"
msgstr "新建议题"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "新建åˆå¹¶è¯·æ±‚"
@@ -1587,8 +2049,23 @@ msgstr "æ–°å­ç¾¤ç»„"
msgid "New tag"
msgstr "新建标签"
-msgid "No container images stored for this project. Add one by following the instructions above."
-msgstr "此项目当å‰æœªå­˜å‚¨å®¹å™¨é•œåƒã€‚如需使用,请å‚照上述说明新建容器镜åƒã€‚"
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
+msgstr ""
msgid "No repository"
msgstr "没有存储库"
@@ -1602,9 +2079,15 @@ msgstr "没有花费时间"
msgid "None"
msgstr "æ— "
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "æ•°æ®ä¸è¶³"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "æ•°æ®ä¸è¶³"
@@ -1665,6 +2148,12 @@ msgstr "关注"
msgid "Notifications"
msgstr "通知"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr "å一"
@@ -1674,8 +2163,8 @@ msgstr "å一月"
msgid "Number of access attempts"
msgstr "å°è¯•è®¿é—®æ¬¡æ•°"
-msgid "Number of failures before backing off"
-msgstr "退出å‰çš„失败次数"
+msgid "OK"
+msgstr ""
msgid "Oct"
msgstr "å"
@@ -1689,6 +2178,9 @@ msgstr "筛选"
msgid "Only project members can comment."
msgstr "åªæœ‰é¡¹ç›®æˆå‘˜å¯ä»¥å‘表评论。"
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr "已打开"
@@ -1722,9 +2214,6 @@ msgstr "« 首页"
msgid "Password"
msgstr "密ç "
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "未ç»è®¸å¯çš„人将永远ä¸ä¼šæ”¶åˆ°é€šçŸ¥å¹¶ä¸”无法评论。"
-
msgid "Pipeline"
msgstr "æµæ°´çº¿"
@@ -1767,12 +2256,6 @@ msgstr "所有"
msgid "PipelineSchedules|Inactive"
msgstr "未å¯ç”¨"
-msgid "PipelineSchedules|Input variable key"
-msgstr "输入å˜é‡å"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "输入å˜é‡å€¼"
-
msgid "PipelineSchedules|Next Run"
msgstr "下次è¿è¡Œæ—¶é—´"
@@ -1782,9 +2265,6 @@ msgstr "æ— "
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "为此æµæ°´çº¿æ供简短æè¿°"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "删除å˜é‡"
-
msgid "PipelineSchedules|Take ownership"
msgstr "å–得所有æƒ"
@@ -1812,6 +2292,12 @@ msgstr "上周的æµæ°´çº¿"
msgid "Pipelines for last year"
msgstr "去年的æµæ°´çº¿"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "所有"
@@ -1824,12 +2310,21 @@ msgstr "于阶段"
msgid "Pipeline|with stages"
msgstr "于阶段"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "请填写验è¯ç ã€‚"
msgid "Preferences"
msgstr "å好设置"
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "ç§äºº - å¿…é¡»å‘æ¯ä¸ªç”¨æˆ·æ˜Žç¡®æŽˆäºˆé¡¹ç›®è®¿é—®æƒé™ã€‚"
@@ -1875,6 +2370,9 @@ msgstr "您的å¸æˆ·ç›®å‰æ˜¯è¿™äº›ç¾¤ç»„的所有者:"
msgid "Profiles|your account"
msgstr "您的å¸æˆ·"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "项目 “%{project_name}†正在被删除。"
@@ -1890,6 +2388,15 @@ msgstr "项目 '%{project_name}' 已更新完æˆã€‚"
msgid "Project access must be granted explicitly to each user."
msgstr "项目访问æƒé™å¿…须明确授æƒç»™æ¯ä¸ªç”¨æˆ·ã€‚"
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "项目详情"
@@ -1908,6 +2415,21 @@ msgstr "项目导出已开始。下载链接将通过电å­é‚®ä»¶å‘é€ã€‚"
msgid "ProjectActivityRSS|Subscribe"
msgstr "订阅"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "åœç”¨"
@@ -1932,15 +2454,9 @@ msgstr "分支图"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "è”系管ç†å‘˜æ›´æ”¹æ­¤è®¾ç½®ã€‚"
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr "ç«‹å³åœ¨é»˜è®¤åˆ†æ”¯ä¸Šè¿è¡Œæµæ°´çº¿"
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr "åªæœ‰å·²ç­¾ç½²æ交æ‰å¯ä»¥æŽ¨é€åˆ°æ­¤å­˜å‚¨åº“。"
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr "设置CI/CD时出现JavaScript问题"
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr "此设置已应用于æœåŠ¡å™¨çº§åˆ«ï¼Œå¯ç”±ç®¡ç†å‘˜è¦†ç›–。"
@@ -2005,11 +2521,14 @@ msgid "PrometheusService|Prometheus API Base URL, like http://prometheus.example
msgstr "Prometheus API 地å€ï¼Œä¾‹å¦‚ http://prometheus.example.com/"
msgid "PrometheusService|Time-series monitoring service"
-msgstr "Prometheus 监测"
+msgstr ""
msgid "PrometheusService|View environments"
msgstr "查看环境"
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "公开 - 群组和任何公共项目å¯ä»¥åœ¨æ²¡æœ‰ä»»ä½•èº«ä»½éªŒè¯çš„情况下查看。"
@@ -2025,6 +2544,9 @@ msgstr "推é€äº‹ä»¶"
msgid "PushRule|Committer restriction"
msgstr "æ交é™åˆ¶"
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "了解更多"
@@ -2037,6 +2559,12 @@ msgstr "分支"
msgid "RefSwitcher|Tags"
msgstr "标签"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr "注册表"
@@ -2061,9 +2589,18 @@ msgstr "相关已åˆå¹¶çš„åˆå¹¶è¯·æ±‚"
msgid "Remind later"
msgstr "ç¨åŽæ醒"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "删除项目"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "存储库"
@@ -2079,6 +2616,10 @@ msgstr "é‡ç½®å¥åº·æ£€æŸ¥è®¿é—®ä»¤ç‰Œ"
msgid "Reset runners registration token"
msgstr "é‡ç½® Runner 注册令牌"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+
msgid "Revert this commit"
msgstr "还原此æ交"
@@ -2088,15 +2629,15 @@ msgstr "还原此åˆå¹¶è¯·æ±‚"
msgid "SSH Keys"
msgstr "SSH 密钥"
-msgid "Save"
-msgstr "ä¿å­˜"
-
msgid "Save changes"
msgstr "ä¿å­˜ä¿®æ”¹"
msgid "Save pipeline schedule"
msgstr "ä¿å­˜æµæ°´çº¿è®¡åˆ’"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "新建æµæ°´çº¿è®¡åˆ’"
@@ -2112,38 +2653,59 @@ msgstr "议题看æ¿èŒƒå›´"
msgid "Search branches and tags"
msgstr "æœç´¢åˆ†æ”¯å’Œæ ‡ç­¾"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "é‡ç½®å¤±è´¥ä¿¡æ¯ç­‰å¾…时间(秒)"
-msgid "Seconds to wait after a storage failure"
-msgstr "存储失败åŽç­‰å¾…时间(秒)"
-
msgid "Seconds to wait for a storage access attempt"
msgstr "等待存储访问å°è¯•æ—¶é—´(秒)"
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "选择下载格å¼"
msgid "Select a timezone"
msgstr "选择时区"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "选择目标分支"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr "ä¹"
msgid "September"
msgstr "ä¹æœˆ"
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr "æœåŠ¡æ¨¡æ¿"
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "为账å·åˆ›å»ºä¸€ä¸ªç”¨äºŽæŽ¨é€æˆ–拉å–çš„ %{protocol} 密ç ã€‚"
-msgid "Set up CI"
-msgstr "设置 CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "设置 Koding"
@@ -2157,6 +2719,15 @@ msgstr "设置密ç "
msgid "Settings"
msgstr "设置"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr "查看上级页é¢"
@@ -2168,10 +2739,7 @@ msgid_plural "Showing %d events"
msgstr[0] "显示 %d 个事件"
msgid "Sidebar|Change weight"
-msgstr "编辑宽度"
-
-msgid "Sidebar|Edit"
-msgstr "编辑"
+msgstr "编辑æƒé‡"
msgid "Sidebar|No"
msgstr "æ— "
@@ -2185,18 +2753,30 @@ msgstr "宽度"
msgid "Snippets"
msgstr "代ç ç‰‡æ®µ"
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr "å‘生了错误。"
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr "è¯•å›¾æ”¹å˜ ${this.issuableDisplayName} çš„é”定状æ€æ—¶å‡ºé”™äº†"
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr "拉å–项目时å‘生错误。"
msgid "Something went wrong while fetching the registry list."
msgstr "拉å–注册表列表时å‘生错误。"
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "排åº"
@@ -2326,12 +2906,12 @@ msgstr "å¯åŠ¨ Runner!"
msgid "Stopped"
msgstr "å·²åœæ­¢"
+msgid "Storage"
+msgstr ""
+
msgid "Subgroups"
msgstr "å­ç¾¤ç»„"
-msgid "Subscribe"
-msgstr "订阅"
-
msgid "Switch branch/tag"
msgstr "切æ¢åˆ†æ”¯/标签"
@@ -2426,8 +3006,11 @@ msgstr "谢谢 ! 请ä¸è¦å†æ˜¾ç¤º"
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr "GitLab 中的高级全局æœç´¢åŠŸèƒ½æ˜¯éžå¸¸å¼ºå¤§çš„æœç´¢æœåŠ¡ã€‚您å¯ä»¥æœç´¢å…¶ä»–团队的代ç ä»¥å¸®åŠ©æ‚¨å®Œå–„自己项目中的代ç ã€‚从而é¿å…创建é‡å¤çš„代ç å’Œæµªè´¹æ—¶é—´ã€‚"
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
-msgstr "断路器关闭阈值应该低于故障计数阈值"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "ç¼–ç é˜¶æ®µæ¦‚述了从第一次æ交到创建åˆå¹¶è¯·æ±‚的时间。创建第一个åˆå¹¶è¯·æ±‚åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -2441,21 +3024,18 @@ msgstr "派生关系已被删除。"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "议题阶段概述了从创建议题到将议题添加到里程碑或议题看æ¿æ‰€èŠ±è´¹çš„时间。创建第一个议题åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„.。"
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "The number of attempts GitLab will make to access a storage."
msgstr "GitLab 访问存储的次数。"
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
-msgstr "GitLab 将临时ç¦ç”¨å¯¹ä¸»å­˜å‚¨åˆ†ç‰‡çš„访问"
-
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "GitLab 将完全阻止访问存储的故障次数。å¯ä»¥åœ¨ç®¡ç†ç•Œé¢%{link_to_health_page}或使用%{api_documentation_link}é‡ç½®æ•…障次数。"
msgid "The phase of the development lifecycle."
msgstr "项目生命周期中的å„个阶段。"
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "æµæ°´çº¿è®¡åˆ’会周期性é‡å¤è¿è¡ŒæŒ‡å®šåˆ†æ”¯æˆ–标签的æµæ°´çº¿ã€‚这些æµæ°´çº¿å°†æ ¹æ®å…¶å…³è”用户继承有é™çš„项目访问æƒé™ã€‚"
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "计划阶段概述了从议题添加到日程到推é€é¦–次æ交的时间。当首次推é€æ交åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -2486,20 +3066,47 @@ msgstr "GitLab å°†ä¿æŒå¤±è´¥ä¿¡æ¯çš„时间(秒)。在此期间ä¸å‘生故障
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "GitLab å°†å°è¯•è®¿é—®å­˜å‚¨çš„时间(秒)。在此时间之åŽå°†å¼•å‘超时错误。"
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "该阶段æ¯æ¡æ•°æ®æ‰€èŠ±çš„时间"
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "中ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—中最中间的值。例如在 3ã€5ã€9 之间,中ä½æ•°æ˜¯ 5。在 3ã€5ã€7ã€8 之间,中ä½æ•°æ˜¯ (5 + 7)/ 2 = 6。"
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "访问 Git 存储时出现问题:"
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr "这个看æ¿çš„范围缩å°äº†"
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
-msgstr "自您开始编辑åŽ, 此分支已更改。您想创建一个新的分支å—?"
+msgid "This directory"
+msgstr ""
msgid "This is a confidential issue."
msgstr "这是一个机密议题。"
@@ -2507,18 +3114,45 @@ msgstr "这是一个机密议题。"
msgid "This is the author's first Merge Request to this project."
msgstr "这是作者为项目贡献的第一个åˆå¹¶è¯·æ±‚。"
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr "这个是机密且已é”定的议题。"
msgid "This issue is locked."
msgstr "此议题已é”定。"
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "在创建一个空的存储库或导入现有存储库之å‰ï¼Œå°†æ— æ³•æŽ¨é€ä»£ç ã€‚"
msgid "This merge request is locked."
msgstr "æ­¤åˆå¹¶è¯·æ±‚å·²é”定。"
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr "这些电å­é‚®ä»¶è‡ªåŠ¨ç”Ÿæˆä¸ºé—®é¢˜(评论生æˆä¸ºç”µå­é‚®ä»¶å¯¹è¯)在这里列出。"
@@ -2531,9 +3165,21 @@ msgstr "开始进行编ç å‰çš„时间"
msgid "Time between merge request creation and merge/close"
msgstr "从创建åˆå¹¶è¯·æ±‚到被åˆå¹¶æˆ–关闭的时间"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "创建第一个åˆå¹¶è¯·æ±‚之å‰çš„时间"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr " %s 天å‰"
@@ -2671,6 +3317,18 @@ msgstr "秒"
msgid "Title"
msgstr "标题"
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "总时间"
@@ -2686,20 +3344,41 @@ msgstr "跟踪活动与贡献的分æžã€‚"
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr "在项目和里程碑之间跟踪共享主题的议题组"
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr "打开æœåŠ¡å°"
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr "解é”"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "已解é”"
msgid "Unstar"
msgstr "å–消星标"
-msgid "Unsubscribe"
-msgstr "退订"
+msgid "Up to date"
+msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "å‡çº§æ‚¨çš„方案以å¯ç”¨é«˜çº§å…¨å±€æœç´¢ã€‚"
@@ -2722,6 +3401,9 @@ msgstr "上传新文件"
msgid "Upload file"
msgstr "上传文件"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "点击上传"
@@ -2734,9 +3416,15 @@ msgstr "在安装过程中使用以下注册令牌:"
msgid "Use your global notification setting"
msgstr "使用全局通知设置"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr "æµè§ˆæ–‡ä»¶ @ "
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "查看待处ç†çš„åˆå¹¶è¯·æ±‚"
@@ -2758,6 +3446,9 @@ msgstr "未知"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "æƒé™ä¸è¶³ã€‚如需查看相关数æ®ï¼Œè¯·å‘管ç†å‘˜ç”³è¯·æƒé™ã€‚"
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "该阶段的数æ®ä¸è¶³ï¼Œæ— æ³•æ˜¾ç¤ºã€‚"
@@ -2770,9 +3461,6 @@ msgstr "如果有新的推é€æˆ–新的议题,Webhook将自动触å‘您设置UR
msgid "Weight"
msgstr "æƒé‡"
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr "访问存储失败时。 GitLab 将在此处指定的时间内阻止对存储的访问。这å…许文件系统æ¢å¤ã€‚故障分片上的存储库暂时无法使用"
-
msgid "Wiki"
msgstr "Wiki"
@@ -2791,6 +3479,12 @@ msgstr "建议安装 %{markdown},以便 GFM 功能在本地渲染:"
msgid "WikiClone|Start Gollum and edit locally"
msgstr "å¯åŠ¨ Gollum 并在本地编辑"
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr "您ä¸èƒ½åˆ›å»º wiki 页é¢"
@@ -2893,9 +3587,21 @@ msgstr "å³å°†åˆ é™¤ä¸Žæºé¡¹ç›® %{forked_from_project} 的派生关系。确定
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "å³å°† %{project_name_with_namespace} 转移给å¦ä¸€ä¸ªæ‰€æœ‰è€…。确定继续å—?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr "您ä¸èƒ½å†™å…¥åªè¯»çš„辅助 GitLab Geo 实例。请改用%{link_to_primary_node}。"
@@ -2935,6 +3641,12 @@ msgstr "在账å·ä¸­ %{add_ssh_key_link} 之å‰å°†æ— æ³•é€šè¿‡ SSH 拉å–或推é
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr "在您的个人资料中添加SSH密钥之å‰ï¼Œæ‚¨ä¸èƒ½é€šè¿‡SSHæ¥æ‹‰å–或推é€é¡¹ç›®ä»£ç ã€‚"
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr "您的评论将ä¸ä¼šå…¬å¼€æ˜¾ç¤ºã€‚"
@@ -2947,25 +3659,218 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr "您的项目"
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr "分支å称"
msgid "by"
msgstr "æ¥è‡ª"
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr "æ交"
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "天"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "新建åˆå¹¶è¯·æ±‚"
msgid "notification emails"
msgstr "通知邮件"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "上级"
@@ -2976,12 +3881,21 @@ msgstr "密ç "
msgid "personal access token"
msgstr "个人访问令牌"
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr "æº"
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr "帮助您的贡献者进行有效沟通ï¼"
msgid "username"
msgstr "用户å"
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index 99024ee527c..c79a46c93f7 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:58-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Traditional, Hong Kong\n"
"Language: zh_HK\n"
@@ -16,20 +16,35 @@ msgstr ""
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] " %d 次æ交"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] ""
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "為æ高é é¢åŠ è¼‰é€Ÿåº¦åŠæ€§èƒ½ï¼Œå·²çœç•¥äº† %s 次æ交。"
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "ç”± %{commit_author_link} æ交於 %{commit_timeago}"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -41,9 +56,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "已失敗 %{number_of_failures} 次,最大失敗 %{maximum_failures} 次,GitLab å°‡é‡è©¦ã€‚"
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "已失敗 %{number_of_failures} 次,最大失敗 %{maximum_failures} 次,GitLab 將在 %{number_of_seconds} 秒後é‡è©¦ã€‚"
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "已失敗 %{number_of_failures} 次,最大失敗 %{maximum_failures} 次,GitLabä¸æœƒé‡è©¦ã€‚當å•é¡Œè§£æ±ºæ™‚é‡ç½®å­˜å„²ä¿¡æ¯ã€‚"
@@ -115,24 +127,81 @@ msgstr "添加許å¯è­‰"
msgid "Add new directory"
msgstr "添加新目錄"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr ""
msgid "All"
msgstr "全部"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr ""
@@ -157,9 +226,6 @@ msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·šè¨ˆåŠƒå—Žï¼Ÿ"
msgid "Are you sure you want to discard your changes?"
msgstr "確定è¦æ”¾æ£„修改嗎?"
-msgid "Are you sure you want to leave this group?"
-msgstr ""
-
msgid "Are you sure you want to reset registration token?"
msgstr "確定è¦é‡ç½®è¨»å†Šä»¤ç‰Œå—Žï¼Ÿ"
@@ -172,6 +238,21 @@ msgstr "確定嗎?"
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放文件到此處或者 %{upload_link}"
@@ -187,13 +268,16 @@ msgstr ""
msgid "Author"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
+msgid "Authors: %{authors}"
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
msgstr ""
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr ""
msgid "AutoDevOps|Auto DevOps (Beta)"
@@ -217,6 +301,12 @@ msgstr ""
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -271,6 +361,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "分支"
@@ -398,8 +491,8 @@ msgstr "作者:"
msgid "CI / CD"
msgstr ""
-msgid "CI configuration"
-msgstr "CI é…ç½®"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr ""
@@ -410,6 +503,9 @@ msgstr "å–消"
msgid "Cancel edit"
msgstr "å–消编辑"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -425,15 +521,24 @@ msgstr "優é¸"
msgid "ChangeTypeAction|Revert"
msgstr "還原"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "更新日誌"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "統計圖"
msgid "Chat"
msgstr ""
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -446,7 +551,19 @@ msgstr "優é¸æ­¤æ交"
msgid "Cherry-pick this merge request"
msgstr "優é¸æ­¤åˆä½µè«‹æ±‚"
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -503,79 +620,91 @@ msgstr "已跳éŽ"
msgid "CiStatus|running"
msgstr "é‹è¡Œä¸­"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgid "CiVariables|Input variable key"
msgstr ""
-msgid "Clone repository"
+msgid "CiVariables|Input variable value"
msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
+msgid "CiVariable|* (All environments)"
msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Click to expand text"
+msgstr ""
+
+msgid "Clone repository"
+msgstr ""
+
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
+msgid "ClusterIntegration|API URL"
msgstr ""
-msgid "ClusterIntegration|Cluster integration"
+msgid "ClusterIntegration|Add Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
+msgid "ClusterIntegration|Applications"
msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Cluster name"
+msgid "ClusterIntegration|CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
+msgstr ""
+
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -584,37 +713,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
+msgid "ClusterIntegration|Copy Token"
msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr ""
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -632,64 +758,94 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr ""
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Machine type"
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
+msgid "ClusterIntegration|Machine type"
msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|Note:"
+msgid "ClusterIntegration|More information"
msgstr ""
-msgid "ClusterIntegration|Number of nodes"
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Note:"
msgstr ""
-msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
+msgid "ClusterIntegration|Number of nodes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the cluster"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
-msgid "ClusterIntegration|Problem setting up the clusters list"
+msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
msgid "ClusterIntegration|Project ID"
@@ -701,16 +857,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr ""
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr ""
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -719,7 +878,7 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|See machine types"
@@ -740,25 +899,25 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
@@ -770,7 +929,7 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
+msgid "ClusterIntegration|check the pricing here"
msgstr ""
msgid "ClusterIntegration|documentation"
@@ -788,6 +947,9 @@ msgstr ""
msgid "ClusterIntegration|properly configured"
msgstr ""
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "è©•è«–"
@@ -804,6 +966,9 @@ msgstr "最近30次æ交花費的時間(分é˜ï¼‰"
msgid "Commit message"
msgstr "æ交信æ¯"
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "æ交"
@@ -816,15 +981,57 @@ msgstr "æ交"
msgid "Commits feed"
msgstr "æ交動態"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "æ­·å²"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "æ交者:"
msgid "Compare"
msgstr "比較"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr ""
@@ -876,6 +1083,9 @@ msgstr "è²¢ç»æŒ‡å—"
msgid "Contributors"
msgstr "è²¢ç»è€…"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -897,9 +1107,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "複製URL到剪貼æ¿"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "複製æ交 SHA 到剪貼æ¿"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "創建新目錄"
@@ -918,6 +1137,9 @@ msgstr ""
msgid "Create file"
msgstr ""
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "創建åˆä½µè«‹æ±‚"
@@ -930,6 +1152,9 @@ msgstr ""
msgid "Create new file"
msgstr ""
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "創建..."
@@ -951,6 +1176,9 @@ msgstr "Cron 時å€"
msgid "Cron syntax"
msgstr "Cron 語法"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "自定義通知事件"
@@ -960,9 +1188,6 @@ msgstr "自定義通知級別繼承自åƒèˆ‡ç´šåˆ¥ã€‚使用自定義通知級別
msgid "Cycle Analytics"
msgstr "週期分æž"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "週期分æžæ¦‚述了項目從想法到產å“實ç¾çš„å„階段所需的時間。"
-
msgid "CycleAnalyticsStage|Code"
msgstr "編碼"
@@ -1018,12 +1243,21 @@ msgstr ""
msgid "Details"
msgstr "詳情"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "目錄å稱"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "放棄更改"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr ""
@@ -1060,15 +1294,24 @@ msgstr "差異文件"
msgid "DownloadSource|Download"
msgstr "下載"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "編輯"
msgid "Edit Pipeline Schedule %{id}"
msgstr "編輯 %{id} æµæ°´ç·šè¨ˆåŠƒ"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr ""
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1087,9 +1330,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1132,9 +1372,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "全部"
@@ -1162,6 +1426,9 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆ 1 日淩晨 4 點)"
msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯é€±åŸ·è¡Œï¼ˆå‘¨æ—¥æ·©æ™¨ 4 點)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr ""
@@ -1180,6 +1447,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr ""
@@ -1223,10 +1493,10 @@ msgstr "從åˆä½µè«‹æ±‚çš„åˆä½µåˆ°éƒ¨ç½²è‡³ç”Ÿç”¢ç’°å¢ƒ"
msgid "GPG Keys"
msgstr ""
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1235,16 +1505,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
+msgstr ""
+
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1253,12 +1607,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Git 存儲å¥åº·ä¿¡æ¯å·²é‡ç½®"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "GitLab Runner 介紹"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "跳轉到派生項目"
@@ -1268,6 +1634,9 @@ msgstr "跳轉到派生項目"
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr ""
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
@@ -1304,7 +1673,7 @@ msgstr ""
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
msgstr ""
msgid "GroupsTree|Create a project in this group."
@@ -1355,6 +1724,10 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ"
msgid "HealthCheck|Unhealthy"
msgstr "ä¸è‰¯"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+
msgid "History"
msgstr ""
@@ -1380,6 +1753,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr ""
@@ -1407,6 +1786,9 @@ msgstr ""
msgid "Issues"
msgstr ""
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1425,6 +1807,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "åœç”¨"
@@ -1434,6 +1837,9 @@ msgstr "啟用"
msgid "Labels"
msgstr ""
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
@@ -1462,6 +1868,9 @@ msgstr "您推é€äº†"
msgid "LastPushEvent|at"
msgstr "在"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "了解更多"
@@ -1480,13 +1889,18 @@ msgstr "退出項目"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "最多顯示 %d 個事件"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr ""
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr ""
@@ -1496,12 +1910,21 @@ msgstr ""
msgid "Login"
msgstr ""
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr ""
@@ -1514,6 +1937,9 @@ msgstr "中ä½æ•¸"
msgid "Members"
msgstr ""
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr ""
@@ -1523,9 +1949,30 @@ msgstr "åˆä½µäº‹ä»¶ (merge event)"
msgid "Merge request"
msgstr ""
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr ""
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "添加壹個 SSH 公鑰"
@@ -1535,16 +1982,28 @@ msgstr ""
msgid "More information is available|here"
msgstr "幫助文檔"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
+msgid "Name new label"
msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "新建議題"
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "創建æµæ°´ç·šè¨ˆåŠƒ"
@@ -1569,6 +2028,9 @@ msgstr ""
msgid "New issue"
msgstr "新議題"
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "新增åˆä½µè«‹æ±‚"
@@ -1587,7 +2049,22 @@ msgstr ""
msgid "New tag"
msgstr "新增標籤"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1602,9 +2079,15 @@ msgstr ""
msgid "None"
msgstr ""
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "ä¸å¯ç”¨"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "數據ä¸è¶³"
@@ -1665,6 +2148,12 @@ msgstr "關注"
msgid "Notifications"
msgstr ""
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1674,7 +2163,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr ""
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1689,6 +2178,9 @@ msgstr "篩é¸"
msgid "Only project members can comment."
msgstr ""
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1722,9 +2214,6 @@ msgstr ""
msgid "Password"
msgstr ""
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr ""
-
msgid "Pipeline"
msgstr "æµæ°´ç·š"
@@ -1767,12 +2256,6 @@ msgstr "所有"
msgid "PipelineSchedules|Inactive"
msgstr "未啟用"
-msgid "PipelineSchedules|Input variable key"
-msgstr "輸入變é‡å"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "輸入變é‡å€¼"
-
msgid "PipelineSchedules|Next Run"
msgstr "下次é‹è¡Œæ™‚é–“"
@@ -1782,9 +2265,6 @@ msgstr "ç„¡"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "為此æµæ°´ç·šæ供簡短æè¿°"
-msgid "PipelineSchedules|Remove variable row"
-msgstr "刪除變é‡"
-
msgid "PipelineSchedules|Take ownership"
msgstr "å–得所有權"
@@ -1812,6 +2292,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "所有"
@@ -1824,12 +2310,21 @@ msgstr "於階段"
msgid "Pipeline|with stages"
msgstr "於階段"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -1875,6 +2370,9 @@ msgstr ""
msgid "Profiles|your account"
msgstr ""
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -1890,6 +2388,15 @@ msgstr "é …ç›® '%{project_name}' 已更新完æˆã€‚"
msgid "Project access must be granted explicitly to each user."
msgstr "項目訪å•æ¬Šé™å¿…須明確授權給æ¯å€‹ç”¨æˆ¶ã€‚"
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "專案詳情"
@@ -1908,6 +2415,21 @@ msgstr "項目導出已開始。下載éˆæŽ¥å°‡é€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚"
msgid "ProjectActivityRSS|Subscribe"
msgstr "訂閱"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "åœç”¨"
@@ -1932,15 +2454,9 @@ msgstr "分支圖"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2010,6 +2526,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr ""
@@ -2025,6 +2544,9 @@ msgstr "推é€äº‹ä»¶ (push event) "
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "了解更多"
@@ -2037,6 +2559,12 @@ msgstr "分支"
msgid "RefSwitcher|Tags"
msgstr "標籤"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2061,9 +2589,18 @@ msgstr "相關已åˆä½µçš„åˆä½µè«‹æ±‚"
msgid "Remind later"
msgstr "ç¨å¾Œæ醒"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "刪除項目"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "存儲庫"
@@ -2079,6 +2616,10 @@ msgstr "é‡ç½®å¥åº·æª¢æŸ¥è¨ªå•ä»¤ç‰Œ"
msgid "Reset runners registration token"
msgstr "é‡ç½® Runner 註冊令牌"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+
msgid "Revert this commit"
msgstr "還原此æ交"
@@ -2088,15 +2629,15 @@ msgstr "還原此åˆä½µè«‹æ±‚"
msgid "SSH Keys"
msgstr ""
-msgid "Save"
-msgstr ""
-
msgid "Save changes"
msgstr ""
msgid "Save pipeline schedule"
msgstr "ä¿å­˜æµæ°´ç·šè¨ˆåŠƒ"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "新建æµæ°´ç·šè¨ˆåŠƒ"
@@ -2112,38 +2653,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "æœç´¢åˆ†æ”¯å’Œæ¨™ç±¤"
-msgid "Seconds before reseting failure information"
+msgid "Search milestones"
msgstr ""
-msgid "Seconds to wait after a storage failure"
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
+msgid "Seconds before reseting failure information"
msgstr ""
msgid "Seconds to wait for a storage access attempt"
msgstr ""
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "é¸æ“‡ä¸‹è¼‰æ ¼å¼"
msgid "Select a timezone"
msgstr "é¸æ“‡æ™‚å€"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯"
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr ""
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "為賬號添加壹個用於推é€æˆ–拉å–çš„ %{protocol} 密碼。"
-msgid "Set up CI"
-msgstr "設置 CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "設置 Koding"
@@ -2157,6 +2719,15 @@ msgstr "設置密碼"
msgid "Settings"
msgstr ""
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -2170,9 +2741,6 @@ msgstr[0] "顯示 %d 個事件"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2185,18 +2753,30 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr ""
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr ""
msgid "Something went wrong while fetching the registry list."
msgstr ""
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr ""
@@ -2326,10 +2906,10 @@ msgstr "é‹ä½œ Runner!"
msgid "Stopped"
msgstr ""
-msgid "Subgroups"
+msgid "Storage"
msgstr ""
-msgid "Subscribe"
+msgid "Subgroups"
msgstr ""
msgid "Switch branch/tag"
@@ -2426,7 +3006,10 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
@@ -2441,10 +3024,10 @@ msgstr "派生關係已被刪除。"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "議題階段概述了從創建議題到將議題添加到è£ç¨‹ç¢‘或議題看æ¿æ‰€èŠ±è²»çš„時間。創建第壹個議題後,數據將自動添加到此處.。"
-msgid "The number of attempts GitLab will make to access a storage."
+msgid "The maximum file size allowed is 200KB."
msgstr ""
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
+msgid "The number of attempts GitLab will make to access a storage."
msgstr ""
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
@@ -2453,9 +3036,6 @@ msgstr ""
msgid "The phase of the development lifecycle."
msgstr "項目生命週期中的å„個階段。"
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "æµæ°´ç·šè¨ˆåŠƒæœƒé€±æœŸæ€§é‡è¤‡é‹è¡ŒæŒ‡å®šåˆ†æ”¯æˆ–標籤的æµæ°´ç·šã€‚這些æµæ°´ç·šå°‡æ ¹æ“šå…¶é—œè¯ç”¨æˆ¶ç¹¼æ‰¿æœ‰é™çš„項目訪å•æ¬Šé™ã€‚"
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "計劃階段概述了從議題添加到日程到推é€é¦–次æ交的時間。當首次推é€æ交後,數據將自動添加到此處。"
@@ -2486,19 +3066,46 @@ msgstr ""
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr ""
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "該階段æ¯æ¢æ•¸æ“šæ‰€èŠ±çš„時間"
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "中ä½æ•¸æ˜¯å£¹å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "è¨ªå• Git 存儲時出ç¾å•é¡Œï¼š"
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
+msgid "This directory"
msgstr ""
msgid "This is a confidential issue."
@@ -2507,18 +3114,45 @@ msgstr ""
msgid "This is the author's first Merge Request to this project."
msgstr ""
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr ""
msgid "This issue is locked."
msgstr ""
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "在創建壹個空的存儲庫或導入ç¾æœ‰å­˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡æ³•æŽ¨é€ä»£ç¢¼ã€‚"
msgid "This merge request is locked."
msgstr ""
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2531,9 +3165,21 @@ msgstr "開始進行編碼å‰çš„時間"
msgid "Time between merge request creation and merge/close"
msgstr "從創建åˆä½µè«‹æ±‚到被åˆä½µæˆ–關閉的時間"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "創建第壹個åˆä½µè«‹æ±‚之å‰çš„時間"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr " %s 天å‰"
@@ -2671,6 +3317,18 @@ msgstr "秒"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "總時間"
@@ -2686,19 +3344,40 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr ""
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr ""
msgid "Unstar"
msgstr "å–消星標"
-msgid "Unsubscribe"
+msgid "Up to date"
msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
@@ -2722,6 +3401,9 @@ msgstr "上傳新文件"
msgid "Upload file"
msgstr "上傳文件"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "點擊上傳"
@@ -2734,9 +3416,15 @@ msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨ä»¥ä¸‹è¨»å†Šä»¤ç‰Œï¼š"
msgid "Use your global notification setting"
msgstr "使用全局通知設置"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr ""
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "查看開啟的åˆä¸¦è«‹æ±‚"
@@ -2758,6 +3446,9 @@ msgstr "未知"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "權é™ä¸è¶³ã€‚如需查看相關數據,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "該階段的數據ä¸è¶³ï¼Œç„¡æ³•é¡¯ç¤ºã€‚"
@@ -2770,9 +3461,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr ""
-
msgid "Wiki"
msgstr ""
@@ -2791,6 +3479,12 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally"
msgstr ""
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr ""
@@ -2893,9 +3587,21 @@ msgstr "å³å°‡åˆªé™¤èˆ‡æºé …ç›® %{forked_from_project} 的派生關系。確定
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "å³å°‡ %{project_name_with_namespace} 轉義給å¦å£¹å€‹æ‰€æœ‰è€…。確定繼續嗎?"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šæ·»åŠ æ–‡ä»¶"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2935,6 +3641,12 @@ msgstr "在賬號中 %{add_ssh_key_link} 之å‰å°‡ç„¡æ³•é€šéŽ SSH 拉å–或推é
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -2947,25 +3659,218 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr ""
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "天"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "新建åˆä½µè«‹æ±‚"
msgid "notification emails"
msgstr "通知郵件"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "父級"
@@ -2976,12 +3881,21 @@ msgstr ""
msgid "personal access token"
msgstr ""
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr ""
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index 14bc24c0e08..635f5c6c449 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -2,8 +2,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gitlab-ee\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2017-12-12 18:31+0000\n"
-"PO-Revision-Date: 2018-01-05 04:42-0500\n"
+"POT-Creation-Date: 2018-02-07 11:38-0600\n"
+"PO-Revision-Date: 2018-02-12 03:58-0500\n"
"Last-Translator: gitlab <mbartlett+crowdin@gitlab.com>\n"
"Language-Team: Chinese Traditional\n"
"Language: zh_TW\n"
@@ -16,20 +16,35 @@ msgstr ""
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
+msgid " and"
+msgstr ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d 個更動 (commit)"
+msgid "%d commit behind"
+msgid_plural "%d commits behind"
+msgstr[0] ""
+
+msgid "%d issue"
+msgid_plural "%d issues"
+msgstr[0] ""
+
msgid "%d layer"
msgid_plural "%d layers"
msgstr[0] "%d 個圖層"
+msgid "%d merge request"
+msgid_plural "%d merge requests"
+msgstr[0] ""
+
msgid "%s additional commit has been omitted to prevent performance issues."
msgid_plural "%s additional commits have been omitted to prevent performance issues."
msgstr[0] "因效能考é‡ï¼Œå·²éš±è— %s 個更動 (commit)。"
-msgid "%{commit_author_link} committed %{commit_timeago}"
-msgstr "%{commit_author_link} 在 %{commit_timeago} é€äº¤"
+msgid "%{commit_author_link} authored %{commit_timeago}"
+msgstr ""
msgid "%{count} participant"
msgid_plural "%{count} participants"
@@ -41,9 +56,6 @@ msgstr ""
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will allow access on the next attempt."
msgstr "ç›®å‰å·²å¤±æ•— %{number_of_failures} 次。GitLab å…許在 %{maximum_failures} 次之內å¯å†å˜—è©¦è®€å– ã€‚"
-msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will block access for %{number_of_seconds} seconds."
-msgstr "已失敗 %{number_of_failures} 次,在失敗 %{maximum_failures} æ¬¡å‰ GitLab 會在 %{number_of_seconds} 秒後é‡è©¦ã€‚"
-
msgid "%{number_of_failures} of %{maximum_failures} failures. GitLab will not retry automatically. Reset storage information when the problem is resolved."
msgstr "已失敗 %{number_of_failures} / %{maximum_failures} 次,GitLab å°‡ä¸å†è‡ªå‹•é‡è©¦ã€‚請在確èªå•é¡Œè§£æ±ºå¾Œæ‰‹å‹•é‡ç½®å„²å­˜ç©ºé–“資訊。"
@@ -115,24 +127,81 @@ msgstr "新增授權æ¢æ¬¾"
msgid "Add new directory"
msgstr "新增目錄"
+msgid "Add todo"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs"
+msgstr ""
+
+msgid "AdminArea|Stop all jobs?"
+msgstr ""
+
+msgid "AdminArea|Stop jobs"
+msgstr ""
+
+msgid "AdminArea|Stopping jobs failed"
+msgstr ""
+
+msgid "AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running."
+msgstr ""
+
msgid "AdminHealthPageLink|health page"
msgstr "系統狀態"
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced settings"
msgstr "進階設定"
msgid "All"
msgstr "全部"
+msgid "All changes are committed"
+msgstr ""
+
+msgid "Allows you to add and manage Kubernetes clusters."
+msgstr ""
+
+msgid "An error occurred previewing the blob"
+msgstr ""
+
msgid "An error occurred when toggling the notification subscription"
msgstr ""
msgid "An error occurred when updating the issue weight"
msgstr ""
+msgid "An error occurred while dismissing the feature highlight. Refresh the page and try dismissing again."
+msgstr ""
+
+msgid "An error occurred while fetching markdown preview"
+msgstr ""
+
msgid "An error occurred while fetching sidebar data"
msgstr ""
+msgid "An error occurred while getting projects"
+msgstr ""
+
+msgid "An error occurred while loading filenames"
+msgstr ""
+
+msgid "An error occurred while rendering KaTeX"
+msgstr ""
+
+msgid "An error occurred while rendering preview broadcast message"
+msgstr ""
+
+msgid "An error occurred while retrieving calendar activity"
+msgstr ""
+
+msgid "An error occurred while retrieving diff"
+msgstr ""
+
+msgid "An error occurred while validating username"
+msgstr ""
+
msgid "An error occurred. Please try again."
msgstr "發生錯誤,請å†è©¦ä¸€æ¬¡ã€‚"
@@ -157,9 +226,6 @@ msgstr "確定è¦åˆªé™¤æ­¤æµæ°´ç·š (pipeline) 排程嗎?"
msgid "Are you sure you want to discard your changes?"
msgstr "確定è¦æ”¾æ£„修改嗎?"
-msgid "Are you sure you want to leave this group?"
-msgstr "確定è¦é›¢é–‹é€™å€‹ç¾¤çµ„嗎?"
-
msgid "Are you sure you want to reset registration token?"
msgstr "確定è¦é‡ç½®è¨»å†Šæ†‘è­‰ (registration token) 嗎?"
@@ -172,6 +238,21 @@ msgstr "確定嗎?"
msgid "Artifacts"
msgstr ""
+msgid "Assign custom color like #FF0000"
+msgstr ""
+
+msgid "Assign labels"
+msgstr ""
+
+msgid "Assign milestone"
+msgstr ""
+
+msgid "Assign to"
+msgstr ""
+
+msgid "Assignee"
+msgstr ""
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放檔案到此處或者 %{upload_link}"
@@ -187,15 +268,18 @@ msgstr "登入紀錄"
msgid "Author"
msgstr "作者"
-msgid "Auto Review Apps and Auto Deploy need a domain name and the %{kubernetes} to work correctly."
-msgstr "自動複閱應用 (review apps) 與自動部署需è¦ç¶²åŸŸå’Œ %{kubernetes} æ‰èƒ½é‹ä½œã€‚"
+msgid "Authors: %{authors}"
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a %{kubernetes} to work correctly."
+msgstr ""
+
+msgid "Auto Review Apps and Auto Deploy need a domain name and a %{kubernetes} to work correctly."
+msgstr ""
msgid "Auto Review Apps and Auto Deploy need a domain name to work correctly."
msgstr "自動複閱應用 (review apps) 與自動部署需è¦ç¶²åŸŸæ‰èƒ½é‹ä½œã€‚"
-msgid "Auto Review Apps and Auto Deploy need the %{kubernetes} to work correctly."
-msgstr "自動複閱應用 (review apps) èˆ‡è‡ªå‹•éƒ¨ç½²éœ€è¦ %{kubernetes} æ‰èƒ½é‹ä½œã€‚"
-
msgid "AutoDevOps|Auto DevOps (Beta)"
msgstr "DevOps 自動化(beta)"
@@ -217,6 +301,12 @@ msgstr "ä½ å¯ä»¥ç‚ºæ­¤å°ˆæ¡ˆå•Ÿå‹• %{link_to_settings}"
msgid "Available"
msgstr ""
+msgid "Avatar will be removed. Are you sure?"
+msgstr ""
+
+msgid "Average per day: %{average}"
+msgstr ""
+
msgid "Billing"
msgstr ""
@@ -271,6 +361,9 @@ msgstr ""
msgid "BillingPlans|per user"
msgstr ""
+msgid "Begin with the selected commit"
+msgstr ""
+
msgid "Branch"
msgid_plural "Branches"
msgstr[0] "分支 (branch) "
@@ -398,8 +491,8 @@ msgstr "作者:"
msgid "CI / CD"
msgstr "CI / CD"
-msgid "CI configuration"
-msgstr "CI 組態"
+msgid "CI/CD configuration"
+msgstr ""
msgid "CICD|Jobs"
msgstr "作業"
@@ -410,6 +503,9 @@ msgstr "å–消"
msgid "Cancel edit"
msgstr "å–消編輯"
+msgid "Cannot modify managed Kubernetes cluster"
+msgstr ""
+
msgid "Change Weight"
msgstr ""
@@ -425,15 +521,24 @@ msgstr "挑é¸"
msgid "ChangeTypeAction|Revert"
msgstr "還原"
+msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
+msgstr ""
+
msgid "Changelog"
msgstr "更新日誌"
+msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
+msgstr ""
+
msgid "Charts"
msgstr "統計圖"
msgid "Chat"
msgstr "å³æ™‚通訊"
+msgid "Check interval"
+msgstr ""
+
msgid "Checking %{text} availability…"
msgstr ""
@@ -446,7 +551,19 @@ msgstr "挑é¸æ­¤æ›´å‹•è¨˜éŒ„ (commit) "
msgid "Cherry-pick this merge request"
msgstr "挑é¸æ­¤åˆä½µè«‹æ±‚ (merge request) "
-msgid "Choose which groups you wish to replicate to this secondary node. Leave blank to replicate all."
+msgid "Choose File ..."
+msgstr ""
+
+msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to see what's changed or to create a merge request."
+msgstr ""
+
+msgid "Choose file..."
+msgstr ""
+
+msgid "Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
msgid "CiStatusLabel|canceled"
@@ -503,79 +620,91 @@ msgstr "已略éŽ"
msgid "CiStatus|running"
msgstr "執行中"
-msgid "CircuitBreakerApiLink|circuitbreaker api"
-msgstr "斷路器 (circuitbreaker) API"
+msgid "CiVariables|Input variable key"
+msgstr ""
-msgid "Clone repository"
-msgstr "複製(clone)檔案庫(repository)"
+msgid "CiVariables|Input variable value"
+msgstr ""
-msgid "Close"
+msgid "CiVariables|Remove variable row"
msgstr ""
-msgid "Cluster"
-msgstr "å¢é›†"
+msgid "CiVariable|* (All environments)"
+msgstr ""
-msgid "ClusterIntegration|%{appList} was successfully installed on your cluster"
+msgid "CiVariable|All environments"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which incur additional costs. See %{pricingLink}"
+msgid "CiVariable|Create wildcard"
msgstr ""
-msgid "ClusterIntegration|API URL"
+msgid "CiVariable|Error occured while saving variables"
msgstr ""
-msgid "ClusterIntegration|Active"
+msgid "CiVariable|New environment"
msgstr ""
-msgid "ClusterIntegration|Add an existing cluster"
+msgid "CiVariable|Protected"
msgstr ""
-msgid "ClusterIntegration|Add cluster"
+msgid "CiVariable|Search environments"
msgstr ""
-msgid "ClusterIntegration|All"
+msgid "CiVariable|Toggle protected"
msgstr ""
-msgid "ClusterIntegration|Applications"
+msgid "CiVariable|Validation failed"
msgstr ""
-msgid "ClusterIntegration|CA Certificate"
+msgid "CircuitBreakerApiLink|circuitbreaker api"
+msgstr "斷路器 (circuitbreaker) API"
+
+msgid "Click to expand text"
msgstr ""
-msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
+msgid "Clone repository"
+msgstr "複製(clone)檔案庫(repository)"
+
+msgid "Close"
msgstr ""
-msgid "ClusterIntegration|Choose how to set up cluster integration"
+msgid "Closed"
msgstr ""
-msgid "ClusterIntegration|Cluster"
+msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Cluster details"
-msgstr "å¢é›†è©³æƒ…"
+msgid "ClusterIntegration|API URL"
+msgstr ""
-msgid "ClusterIntegration|Cluster integration"
-msgstr "å¢é›†æ•´åˆ"
+msgid "ClusterIntegration|Add Kubernetes cluster"
+msgstr ""
-msgid "ClusterIntegration|Cluster integration is disabled for this project."
-msgstr "此專案已經ç¦ç”¨å¢é›†æ•´åˆ"
+msgid "ClusterIntegration|Add an existing Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
+msgstr ""
+
+msgid "ClusterIntegration|Applications"
+msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project."
-msgstr "此專案已經啟用å¢é›†æ•´åˆ"
+msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
+msgstr ""
-msgid "ClusterIntegration|Cluster integration is enabled for this project. Disabling this integration will not affect your cluster, it will only temporarily turn off GitLab's connection to it."
-msgstr "此專案已啟用å¢é›†æ•´åˆã€‚ç¦æ­¢å¢é›†æ•´åˆä¸æœƒå½±éŸ¿æ‚¨çš„å¢é›†ï¼Œå®ƒåªæ˜¯æš«æ™‚關閉 GitLab 的連接。"
+msgid "ClusterIntegration|CA Certificate"
+msgstr ""
-msgid "ClusterIntegration|Cluster is being created on Google Kubernetes Engine..."
+msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr ""
-msgid "ClusterIntegration|Cluster name"
-msgstr "å¢é›†å稱"
+msgid "ClusterIntegration|Choose how to set up Kubernetes cluster integration"
+msgstr ""
-msgid "ClusterIntegration|Cluster was successfully created on Google Kubernetes Engine. Refresh the page to see cluster's details"
+msgid "ClusterIntegration|Choose which of your project's environments will use this Kubernetes cluster."
msgstr ""
-msgid "ClusterIntegration|Clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgid "ClusterIntegration|Control how your Kubernetes cluster integrates with GitLab"
msgstr ""
msgid "ClusterIntegration|Copy API URL"
@@ -584,37 +713,34 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Token"
+msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
-msgid "ClusterIntegration|Copy cluster name"
-msgstr "複製å¢é›†å稱"
+msgid "ClusterIntegration|Copy Token"
+msgstr ""
-msgid "ClusterIntegration|Create a new cluster on Google Engine right from GitLab"
+msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Create cluster"
-msgstr "建立å¢é›†"
+msgid "ClusterIntegration|Create Kubernetes cluster on Google Kubernetes Engine"
+msgstr ""
-msgid "ClusterIntegration|Create cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Create a new Kubernetes cluster on Google Kubernetes Engine right from GitLab"
msgstr ""
msgid "ClusterIntegration|Create on GKE"
msgstr ""
-msgid "ClusterIntegration|Enable cluster integration"
-msgstr "å•Ÿå‹•å¢é›†æ•´åˆ"
-
msgid "ClusterIntegration|Enter the details for an existing Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Enter the details for your cluster"
+msgid "ClusterIntegration|Enter the details for your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|Environment pattern"
+msgid "ClusterIntegration|Environment scope"
msgstr ""
-msgid "ClusterIntegration|GKE pricing"
+msgid "ClusterIntegration|GitLab Integration"
msgstr ""
msgid "ClusterIntegration|GitLab Runner"
@@ -632,46 +758,82 @@ msgstr ""
msgid "ClusterIntegration|Helm Tiller"
msgstr ""
-msgid "ClusterIntegration|Inactive"
-msgstr ""
-
msgid "ClusterIntegration|Ingress"
msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install applications on your cluster. Read more about %{helpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
-msgid "ClusterIntegration|Integrate cluster automation"
+msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
+msgstr ""
+
+msgid "ClusterIntegration|Integration status"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is disabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster integration is enabled for this project. Disabling this integration will not affect your Kubernetes cluster, it will only temporarily turn off GitLab's connection to it."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster name"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes cluster was successfully created on Google Kubernetes Engine. Refresh the page to see Kubernetes cluster's details"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters allow you to use review apps, deploy your applications, run your pipelines, and much more in an easy way. %{link_to_help_page}"
+msgstr ""
+
+msgid "ClusterIntegration|Kubernetes clusters can be used to deploy applications and to provide Review Apps for this project"
msgstr ""
msgid "ClusterIntegration|Learn more about %{link_to_documentation}"
msgstr "學習更多有關於%{link_to_documentation}"
-msgid "ClusterIntegration|Learn more about Clusters"
+msgid "ClusterIntegration|Learn more about Kubernetes"
+msgstr ""
+
+msgid "ClusterIntegration|Learn more about environments"
msgstr ""
msgid "ClusterIntegration|Machine type"
msgstr "機器型別"
-msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create clusters"
-msgstr "請確èªæ‚¨çš„帳戶中%{link_to_requirements} 是å¦å»ºç«‹å¢é›†"
+msgid "ClusterIntegration|Make sure your account %{link_to_requirements} to create Kubernetes clusters"
+msgstr ""
-msgid "ClusterIntegration|Manage cluster integration on your GitLab project"
+msgid "ClusterIntegration|Manage"
msgstr ""
-msgid "ClusterIntegration|Manage your cluster by visiting %{link_gke}"
-msgstr "請至 %{link_gke} 管ç†ä½ çš„å¢é›†"
+msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
+msgstr ""
-msgid "ClusterIntegration|Multiple clusters are available in GitLab Entreprise Edition Premium and Ultimate"
+msgid "ClusterIntegration|More information"
+msgstr ""
+
+msgid "ClusterIntegration|Multiple Kubernetes clusters are available in GitLab Enterprise Edition Premium and Ultimate"
msgstr ""
msgid "ClusterIntegration|Note:"
@@ -680,18 +842,12 @@ msgstr ""
msgid "ClusterIntegration|Number of nodes"
msgstr "所有的端點數é‡"
-msgid "ClusterIntegration|Please enter access information for your cluster. If you need help, you can read our %{link_to_help_page} on clusters"
+msgid "ClusterIntegration|Please enter access information for your Kubernetes cluster. If you need help, you can read our %{link_to_help_page} on Kubernetes"
msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "請確èªä½ çš„ Google 帳號是å¦ç¬¦åˆé€™äº›æ¢ä»¶"
-msgid "ClusterIntegration|Problem setting up the cluster"
-msgstr ""
-
-msgid "ClusterIntegration|Problem setting up the clusters list"
-msgstr ""
-
msgid "ClusterIntegration|Project ID"
msgstr ""
@@ -701,16 +857,19 @@ msgstr ""
msgid "ClusterIntegration|Project namespace (optional, unique)"
msgstr "專案命å空間(é¸å¡«ï¼Œä¸å¯é‡è¤‡ï¼‰"
-msgid "ClusterIntegration|Read our %{link_to_help_page} on cluster integration."
+msgid "ClusterIntegration|Prometheus"
msgstr ""
-msgid "ClusterIntegration|Remove cluster integration"
-msgstr "刪除å¢é›†æ•´åˆ"
+msgid "ClusterIntegration|Read our %{link_to_help_page} on Kubernetes cluster integration."
+msgstr ""
+
+msgid "ClusterIntegration|Remove Kubernetes cluster integration"
+msgstr ""
msgid "ClusterIntegration|Remove integration"
msgstr "刪除整åˆ"
-msgid "ClusterIntegration|Removing cluster integration will remove the cluster configuration you have added to this project. It will not delete your cluster on Google Kubernetes Engine."
+msgid "ClusterIntegration|Remove this Kubernetes cluster's configuration from this project. This will not delete your actual Kubernetes cluster."
msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
@@ -719,8 +878,8 @@ msgstr ""
msgid "ClusterIntegration|Save changes"
msgstr ""
-msgid "ClusterIntegration|See and edit the details for your cluster"
-msgstr "查看與編輯你的å¢é›†å…§å®¹"
+msgid "ClusterIntegration|See and edit the details for your Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|See machine types"
msgstr "查看機器型別"
@@ -740,26 +899,26 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "內部發生了錯誤"
-msgid "ClusterIntegration|Something went wrong while creating your cluster on Google Kubernetes Engine"
+msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr ""
-msgid "ClusterIntegration|There are no clusters to show"
+msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr ""
-msgid "ClusterIntegration|This account must have permissions to create a cluster in the %{link_to_container_project} specified below"
+msgid "ClusterIntegration|Toggle Kubernetes Cluster"
msgstr ""
-msgid "ClusterIntegration|Toggle Cluster"
-msgstr "å¢é›†é–‹é—œ"
+msgid "ClusterIntegration|Toggle Kubernetes cluster"
+msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
-msgid "ClusterIntegration|With a cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
-msgstr "當å¢é›†é€£çµåˆ°æ­¤å°ˆæ¡ˆï¼Œä½ å¯ä»¥ä½¿ç”¨è¤‡é–±æ‡‰ç”¨ (review apps),部署你的應用程å¼ï¼ŒåŸ·è¡Œä½ çš„æµæ°´ç·š (pipelines),還有更多容易上手的方å¼å¯ä»¥ä½¿ç”¨ã€‚"
+msgid "ClusterIntegration|With a Kubernetes cluster associated to this project, you can use review apps, deploy your applications, run your pipelines, and much more in an easy way."
+msgstr ""
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr ""
@@ -770,8 +929,8 @@ msgstr "å€åŸŸ"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|cluster"
-msgstr "å¢é›†"
+msgid "ClusterIntegration|check the pricing here"
+msgstr ""
msgid "ClusterIntegration|documentation"
msgstr ""
@@ -788,6 +947,9 @@ msgstr "符åˆéœ€æ±‚"
msgid "ClusterIntegration|properly configured"
msgstr "設定正確"
+msgid "Collapse"
+msgstr ""
+
msgid "Comments"
msgstr "留言"
@@ -804,6 +966,9 @@ msgstr "最近 30 次更動所花費的時間(分é˜ï¼‰"
msgid "Commit message"
msgstr "更動說明 (commit) "
+msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
+msgstr ""
+
msgid "CommitBoxTitle|Commit"
msgstr "é€äº¤"
@@ -816,15 +981,57 @@ msgstr "更動記錄 (commit) "
msgid "Commits feed"
msgstr "æ›´å‹•æ‘˜è¦ (commit feed)"
+msgid "Commits per day hour (UTC)"
+msgstr ""
+
+msgid "Commits per day of month"
+msgstr ""
+
+msgid "Commits per weekday"
+msgstr ""
+
+msgid "Commits|An error occurred while fetching merge requests data."
+msgstr ""
+
+msgid "Commits|Commit: %{commitText}"
+msgstr ""
+
msgid "Commits|History"
msgstr "更動紀錄 (commit)"
+msgid "Commits|No related merge requests found"
+msgstr ""
+
msgid "Committed by"
msgstr "é€äº¤è€…為 "
msgid "Compare"
msgstr "比較"
+msgid "Compare Git revisions"
+msgstr ""
+
+msgid "Compare Revisions"
+msgstr ""
+
+msgid "CompareBranches|%{source_branch} and %{target_branch} are the same."
+msgstr ""
+
+msgid "CompareBranches|Compare"
+msgstr ""
+
+msgid "CompareBranches|Source"
+msgstr ""
+
+msgid "CompareBranches|Target"
+msgstr ""
+
+msgid "CompareBranches|There isn't anything to compare."
+msgstr ""
+
+msgid "Confidentiality"
+msgstr ""
+
msgid "Container Registry"
msgstr "Container Registry"
@@ -876,6 +1083,9 @@ msgstr "å”作指å—"
msgid "Contributors"
msgstr "å”作者"
+msgid "ContributorsPage|%{startDate} – %{endDate}"
+msgstr ""
+
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -897,9 +1107,18 @@ msgstr ""
msgid "Copy URL to clipboard"
msgstr "複製網å€åˆ°å‰ªè²¼ç°¿"
+msgid "Copy branch name to clipboard"
+msgstr ""
+
msgid "Copy commit SHA to clipboard"
msgstr "複製更動記錄 (commit) 的 SHA 值到剪貼簿"
+msgid "Copy reference to clipboard"
+msgstr ""
+
+msgid "Create"
+msgstr ""
+
msgid "Create New Directory"
msgstr "建立新目錄"
@@ -918,6 +1137,9 @@ msgstr ""
msgid "Create file"
msgstr "新增檔案"
+msgid "Create lists from labels. Issues with that label appear in that list."
+msgstr ""
+
msgid "Create merge request"
msgstr "發出åˆä½µè«‹æ±‚ (merge request) "
@@ -930,6 +1152,9 @@ msgstr "新增資料夾"
msgid "Create new file"
msgstr "新增檔案"
+msgid "Create new label"
+msgstr ""
+
msgid "Create new..."
msgstr "建立..."
@@ -951,6 +1176,9 @@ msgstr "Cron 時å€"
msgid "Cron syntax"
msgstr "Cron 語法"
+msgid "Current node"
+msgstr ""
+
msgid "Custom notification events"
msgstr "自訂事件通知"
@@ -960,9 +1188,6 @@ msgstr "自訂通知的等級與åƒèˆ‡åº¦è¨­å®šç›¸åŒã€‚使用自訂通知讓你
msgid "Cycle Analytics"
msgstr "週期分æž"
-msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
-msgstr "週期分æžè®“ä½ å¯ä»¥æœ‰æ•ˆåœ°é‡æ¸…專案從發想到產å“推出所花費的時間。"
-
msgid "CycleAnalyticsStage|Code"
msgstr "程å¼é–‹ç™¼"
@@ -1018,12 +1243,21 @@ msgstr ""
msgid "Details"
msgstr "細節"
+msgid "Diffs|No file name available"
+msgstr ""
+
msgid "Directory name"
msgstr "目錄å稱"
+msgid "Disable"
+msgstr ""
+
msgid "Discard changes"
msgstr "放棄修改"
+msgid "Discover GitLab Geo."
+msgstr ""
+
msgid "Dismiss Cycle Analytics introduction box"
msgstr "關閉循環分æžä»‹ç´¹è¦–窗"
@@ -1060,15 +1294,24 @@ msgstr "差異檔 (diff)"
msgid "DownloadSource|Download"
msgstr "下載原始碼"
+msgid "Due date"
+msgstr ""
+
msgid "Edit"
msgstr "編輯"
msgid "Edit Pipeline Schedule %{id}"
msgstr "編輯 %{id} æµæ°´ç·š (pipeline) 排程"
+msgid "Edit files in the editor and commit changes here"
+msgstr ""
+
msgid "Emails"
msgstr "é›»å­éƒµä»¶"
+msgid "Enable"
+msgstr ""
+
msgid "Environments|An error occurred while fetching the environments."
msgstr ""
@@ -1087,9 +1330,6 @@ msgstr ""
msgid "Environments|Environments"
msgstr ""
-msgid "Environments|Environments are places where code gets deployed, such as staging or production."
-msgstr ""
-
msgid "Environments|Job"
msgstr ""
@@ -1132,9 +1372,33 @@ msgstr ""
msgid "Error creating epic"
msgstr ""
+msgid "Error fetching contributors data."
+msgstr ""
+
+msgid "Error fetching labels."
+msgstr ""
+
+msgid "Error fetching network graph."
+msgstr ""
+
+msgid "Error fetching refs"
+msgstr ""
+
+msgid "Error fetching usage ping data."
+msgstr ""
+
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error saving label update."
+msgstr ""
+
+msgid "Error updating status for all todos."
+msgstr ""
+
+msgid "Error updating todo status."
+msgstr ""
+
msgid "EventFilterBy|Filter by all"
msgstr "顯示全部"
@@ -1162,6 +1426,9 @@ msgstr "æ¯æœˆåŸ·è¡Œï¼ˆæ¯æœˆä¸€æ—¥æ·©æ™¨å››é»žï¼‰"
msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯é€±åŸ·è¡Œï¼ˆé€±æ—¥æ·©æ™¨ 四點)"
+msgid "Expand"
+msgstr ""
+
msgid "Explore projects"
msgstr "ç€è¦½å°ˆæ¡ˆ"
@@ -1180,6 +1447,9 @@ msgstr ""
msgid "February"
msgstr ""
+msgid "Fields on this page are now uneditable, you can configure"
+msgstr ""
+
msgid "File name"
msgstr "檔案å稱"
@@ -1223,10 +1493,10 @@ msgstr "從請求被åˆä½µå¾Œ (merge request merged) 直到部署至營é‹ç’°å¢ƒ
msgid "GPG Keys"
msgstr "GPG 金鑰"
-msgid "Geo Nodes"
+msgid "Generate a default set of labels"
msgstr ""
-msgid "GeoNodeSyncStatus|Failed"
+msgid "Geo Nodes"
msgstr ""
msgid "GeoNodeSyncStatus|Node is failing or broken."
@@ -1235,16 +1505,100 @@ msgstr ""
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr ""
-msgid "GeoNodeSyncStatus|Out of sync"
+msgid "GeoNodes|Database replication lag:"
+msgstr ""
+
+msgid "GeoNodes|Disabling a node stops the sync process. Are you sure?"
+msgstr ""
+
+msgid "GeoNodes|Does not match the primary storage configuration"
+msgstr ""
+
+msgid "GeoNodes|Failed"
+msgstr ""
+
+msgid "GeoNodes|Full"
+msgstr ""
+
+msgid "GeoNodes|GitLab version does not match the primary node version"
+msgstr ""
+
+msgid "GeoNodes|GitLab version:"
+msgstr ""
+
+msgid "GeoNodes|Health status:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID processed by cursor:"
+msgstr ""
+
+msgid "GeoNodes|Last event ID seen from primary:"
+msgstr ""
+
+msgid "GeoNodes|Loading nodes"
+msgstr ""
+
+msgid "GeoNodes|Local Attachments:"
msgstr ""
-msgid "GeoNodeSyncStatus|Synced"
+msgid "GeoNodes|Local LFS objects:"
+msgstr ""
+
+msgid "GeoNodes|Local job artifacts:"
+msgstr ""
+
+msgid "GeoNodes|New node"
+msgstr ""
+
+msgid "GeoNodes|Out of sync"
+msgstr ""
+
+msgid "GeoNodes|Replication slot WAL:"
+msgstr ""
+
+msgid "GeoNodes|Replication slots:"
+msgstr ""
+
+msgid "GeoNodes|Repositories:"
+msgstr ""
+
+msgid "GeoNodes|Selective"
+msgstr ""
+
+msgid "GeoNodes|Storage config:"
+msgstr ""
+
+msgid "GeoNodes|Sync settings:"
+msgstr ""
+
+msgid "GeoNodes|Synced"
+msgstr ""
+
+msgid "GeoNodes|Unused slots"
+msgstr ""
+
+msgid "GeoNodes|Used slots"
+msgstr ""
+
+msgid "GeoNodes|Wikis:"
+msgstr ""
+
+msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
+msgstr ""
+
+msgid "Geo|All projects"
msgstr ""
msgid "Geo|File sync capacity"
msgstr ""
-msgid "Geo|Groups to replicate"
+msgid "Geo|Groups to synchronize"
+msgstr ""
+
+msgid "Geo|Projects in certain groups"
+msgstr ""
+
+msgid "Geo|Projects in certain storage shards"
msgstr ""
msgid "Geo|Repository sync capacity"
@@ -1253,12 +1607,24 @@ msgstr ""
msgid "Geo|Select groups to replicate."
msgstr ""
+msgid "Geo|Shards to synchronize"
+msgstr ""
+
+msgid "Git revision"
+msgstr ""
+
msgid "Git storage health information has been reset"
msgstr "Git 儲存空間å¥åº·æŒ‡æ•¸å·²é‡ç½®"
+msgid "Git version"
+msgstr ""
+
msgid "GitLab Runner section"
msgstr "GitLab Runner"
+msgid "Gitaly Servers"
+msgstr ""
+
msgid "Go to your fork"
msgstr "å‰å¾€æ‚¨çš„分支 (fork) "
@@ -1268,6 +1634,9 @@ msgstr "å‰å¾€æ‚¨çš„分支 (fork) "
msgid "Google authentication is not %{link_to_documentation}. Ask your GitLab administrator if you want to use this service."
msgstr "Google 身份驗證ä¸æ˜¯ %{link_to_documentation}。如果您想使用此æœå‹™ï¼Œè«‹è«®è©¢ç®¡ç†å“¡ã€‚"
+msgid "Got it!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "ç¦æ­¢èˆ‡å…¶ä»–群組共享 %{group} 中的專案"
@@ -1304,8 +1673,8 @@ msgstr "找ä¸åˆ°ç¾¤çµ„"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr "ä½ å¯ä»¥ç®¡ç†ç¾¤çµ„內所有æˆå“¡çš„æ¯å€‹å°ˆæ¡ˆçš„å­˜å–權é™"
-msgid "GroupsTree|Are you sure you want to leave the \"${this.group.fullName}\" group?"
-msgstr "你確定è¦é›¢é–‹ç¾¤çµ„ \"${this.group.fullName}\" 嗎?"
+msgid "GroupsTree|Are you sure you want to leave the \"${group.fullName}\" group?"
+msgstr ""
msgid "GroupsTree|Create a project in this group."
msgstr "在此群組建立新的專案"
@@ -1355,6 +1724,10 @@ msgstr "沒有檢測到å¥åº·å•é¡Œ"
msgid "HealthCheck|Unhealthy"
msgstr "ä¸è‰¯"
+msgid "Hide value"
+msgid_plural "Hide values"
+msgstr[0] ""
+
msgid "History"
msgstr "æ­·å²"
@@ -1380,6 +1753,12 @@ msgid "Instance"
msgid_plural "Instances"
msgstr[0] ""
+msgid "Instance does not support multiple Kubernetes clusters"
+msgstr ""
+
+msgid "Interested parties can even contribute by pushing commits if they want to."
+msgstr ""
+
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "內部 - 任何登入的使用者都å¯ä»¥æŸ¥çœ‹è©²ç¾¤çµ„åŠå…¶å°ˆæ¡ˆ"
@@ -1407,6 +1786,9 @@ msgstr ""
msgid "Issues"
msgstr "議題"
+msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
+msgstr ""
+
msgid "Jan"
msgstr ""
@@ -1425,6 +1807,27 @@ msgstr ""
msgid "June"
msgstr ""
+msgid "Kubernetes"
+msgstr ""
+
+msgid "Kubernetes Cluster"
+msgstr ""
+
+msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
+msgstr ""
+
+msgid "Kubernetes cluster integration was not removed."
+msgstr ""
+
+msgid "Kubernetes cluster integration was successfully removed."
+msgstr ""
+
+msgid "Kubernetes cluster was successfully updated."
+msgstr ""
+
+msgid "Kubernetes service integration has been deprecated. %{deprecated_message_content} your Kubernetes clusters using the new <a href=\"%{url}\"/>Kubernetes Clusters</a> page"
+msgstr ""
+
msgid "LFSStatus|Disabled"
msgstr "åœç”¨"
@@ -1434,6 +1837,9 @@ msgstr "啟用"
msgid "Labels"
msgstr "標籤"
+msgid "Labels can be applied to issues and merge requests to categorize them."
+msgstr ""
+
msgid "Last %d day"
msgid_plural "Last %d days"
msgstr[0] "最近 %d 天"
@@ -1462,6 +1868,9 @@ msgstr "您上傳 (push) 了"
msgid "LastPushEvent|at"
msgstr "æ–¼"
+msgid "Learn more"
+msgstr ""
+
msgid "Learn more in the"
msgstr "了解更多"
@@ -1480,13 +1889,18 @@ msgstr "退出專案"
msgid "License"
msgstr ""
-msgid "Limited to showing %d event at most"
-msgid_plural "Limited to showing %d events at most"
-msgstr[0] "é™åˆ¶æœ€å¤šé¡¯ç¤º %d 個事件"
+msgid "Loading the GitLab IDE..."
+msgstr ""
msgid "Lock"
msgstr "鎖定"
+msgid "Lock %{issuableDisplayName}"
+msgstr ""
+
+msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
+msgstr ""
+
msgid "Locked"
msgstr "鎖定"
@@ -1496,12 +1910,21 @@ msgstr ""
msgid "Login"
msgstr "登入"
+msgid "Make everyone on your team more productive regardless of their location. GitLab Geo creates read-only mirrors of your GitLab instance so you can reduce the time it takes to clone and fetch large repos."
+msgstr ""
+
+msgid "Manage labels"
+msgstr ""
+
msgid "Mar"
msgstr ""
msgid "March"
msgstr ""
+msgid "Mark done"
+msgstr ""
+
msgid "Maximum git storage failures"
msgstr "最大 git 儲存失敗"
@@ -1514,6 +1937,9 @@ msgstr "中ä½æ•¸"
msgid "Members"
msgstr "æˆå“¡"
+msgid "Merge Request"
+msgstr ""
+
msgid "Merge Requests"
msgstr "åˆä½µè«‹æ±‚ (merge request)"
@@ -1523,9 +1949,30 @@ msgstr "åˆä½µ (merge) 事件"
msgid "Merge request"
msgstr "åˆä½µè«‹æ±‚"
+msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
+msgstr ""
+
+msgid "Merged"
+msgstr ""
+
msgid "Messages"
msgstr "公告"
+msgid "Milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone"
+msgstr ""
+
+msgid "Milestones|Delete milestone %{milestoneTitle}?"
+msgstr ""
+
+msgid "Milestones|Failed to delete milestone %{milestoneTitle}"
+msgstr ""
+
+msgid "Milestones|Milestone %{milestoneTitle} was not found"
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr "新增 SSH 金鑰"
@@ -1535,16 +1982,28 @@ msgstr "監控"
msgid "More information is available|here"
msgstr "å¥åº·æª¢æŸ¥"
+msgid "Move"
+msgstr ""
+
+msgid "Move issue"
+msgstr ""
+
msgid "Multiple issue boards"
msgstr ""
-msgid "New Cluster"
-msgstr "æ–°å¢é›†"
+msgid "Name new label"
+msgstr ""
msgid "New Issue"
msgid_plural "New Issues"
msgstr[0] "建立議題 (issue) "
+msgid "New Kubernetes Cluster"
+msgstr ""
+
+msgid "New Kubernetes cluster"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "建立æµæ°´ç·š (pipeline) 排程"
@@ -1569,6 +2028,9 @@ msgstr "新群組"
msgid "New issue"
msgstr "新增議題 (issue) "
+msgid "New label"
+msgstr ""
+
msgid "New merge request"
msgstr "新增åˆä½µè«‹æ±‚ (merge request) "
@@ -1587,7 +2049,22 @@ msgstr "æ–°å­ç¾¤çµ„"
msgid "New tag"
msgstr "新增標籤"
-msgid "No container images stored for this project. Add one by following the instructions above."
+msgid "No assignee"
+msgstr ""
+
+msgid "No changes"
+msgstr ""
+
+msgid "No connection could be made to a Gitaly Server, please check your logs!"
+msgstr ""
+
+msgid "No due date"
+msgstr ""
+
+msgid "No estimate or time spent"
+msgstr ""
+
+msgid "No file chosen"
msgstr ""
msgid "No repository"
@@ -1602,9 +2079,15 @@ msgstr ""
msgid "None"
msgstr "ç„¡"
+msgid "Not allowed to merge"
+msgstr ""
+
msgid "Not available"
msgstr "無法使用"
+msgid "Not confidential"
+msgstr ""
+
msgid "Not enough data"
msgstr "資料ä¸è¶³"
@@ -1665,6 +2148,12 @@ msgstr "關注"
msgid "Notifications"
msgstr "通知"
+msgid "Notifications off"
+msgstr ""
+
+msgid "Notifications on"
+msgstr ""
+
msgid "Nov"
msgstr ""
@@ -1674,7 +2163,7 @@ msgstr ""
msgid "Number of access attempts"
msgstr "嘗試存å–的次數"
-msgid "Number of failures before backing off"
+msgid "OK"
msgstr ""
msgid "Oct"
@@ -1689,6 +2178,9 @@ msgstr "篩é¸"
msgid "Only project members can comment."
msgstr "åªæœ‰ç¾¤çµ„æˆå“¡æ‰èƒ½ç•™è¨€ã€‚"
+msgid "Open"
+msgstr ""
+
msgid "Opened"
msgstr ""
@@ -1722,9 +2214,6 @@ msgstr "« 第一é "
msgid "Password"
msgstr "密碼"
-msgid "People without permission will never get a notification and won\\'t be able to comment."
-msgstr "沒有權é™çš„使用者將ä¸æœƒæ”¶åˆ°é€šçŸ¥ï¼Œä¹Ÿç„¡æ³•ç•™è¨€ã€‚"
-
msgid "Pipeline"
msgstr "æµæ°´ç·š (pipeline) "
@@ -1767,12 +2256,6 @@ msgstr "所有"
msgid "PipelineSchedules|Inactive"
msgstr "未啟用"
-msgid "PipelineSchedules|Input variable key"
-msgstr "變數å稱"
-
-msgid "PipelineSchedules|Input variable value"
-msgstr "變數值"
-
msgid "PipelineSchedules|Next Run"
msgstr "下次執行時間"
@@ -1782,9 +2265,6 @@ msgstr "ç„¡"
msgid "PipelineSchedules|Provide a short description for this pipeline"
msgstr "請簡單說明此æµæ°´ç·š (pipeline) "
-msgid "PipelineSchedules|Remove variable row"
-msgstr "刪除變數"
-
msgid "PipelineSchedules|Take ownership"
msgstr "å–得所有權"
@@ -1812,6 +2292,12 @@ msgstr "上週的æµæ°´ç·š"
msgid "Pipelines for last year"
msgstr "去年的æµæ°´ç·š"
+msgid "Pipelines|Build with confidence"
+msgstr ""
+
+msgid "Pipelines|Get started with Pipelines"
+msgstr ""
+
msgid "Pipeline|all"
msgstr "所有"
@@ -1824,12 +2310,21 @@ msgstr "於階段"
msgid "Pipeline|with stages"
msgstr "於階段"
+msgid "Play"
+msgstr ""
+
+msgid "Please <a href=%{link_to_billing} target=\"_blank\" rel=\"noopener noreferrer\">enable billing for one of your projects to be able to create a Kubernetes cluster</a>, then try again."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
msgid "Preferences"
msgstr "å好設定"
+msgid "Primary"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr "ç§æœ‰ - 專案權é™å¿…須一一指派給æ¯å€‹ä½¿ç”¨è€…"
@@ -1875,6 +2370,9 @@ msgstr "你的帳號目å‰æ“有這些群組:"
msgid "Profiles|your account"
msgstr "你的帳號"
+msgid "Programming languages used in this repository"
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "專案 \"%{project_name}\" 正在被刪除。"
@@ -1890,6 +2388,15 @@ msgstr "專案 '%{project_name}' 更新完æˆã€‚"
msgid "Project access must be granted explicitly to each user."
msgstr "專案權é™å¿…須一一指派給æ¯å€‹ä½¿ç”¨è€…。"
+msgid "Project avatar"
+msgstr ""
+
+msgid "Project avatar in repository: %{link}"
+msgstr ""
+
+msgid "Project cache successfully reset."
+msgstr ""
+
msgid "Project details"
msgstr "專案細節"
@@ -1908,6 +2415,21 @@ msgstr "專案導出已開始。完æˆå¾Œä¸‹è¼‰é€£çµæœƒé€åˆ°æ‚¨çš„信箱。"
msgid "ProjectActivityRSS|Subscribe"
msgstr "訂閱"
+msgid "ProjectCreationLevel|Allowed to create projects"
+msgstr ""
+
+msgid "ProjectCreationLevel|Default project creation protection"
+msgstr ""
+
+msgid "ProjectCreationLevel|Developers + Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|Masters"
+msgstr ""
+
+msgid "ProjectCreationLevel|No one"
+msgstr ""
+
msgid "ProjectFeature|Disabled"
msgstr "åœç”¨"
@@ -1932,15 +2454,9 @@ msgstr "分支圖"
msgid "ProjectSettings|Contact an admin to change this setting."
msgstr ""
-msgid "ProjectSettings|Immediately run a pipeline on the default branch"
-msgstr ""
-
msgid "ProjectSettings|Only signed commits can be pushed to this repository."
msgstr ""
-msgid "ProjectSettings|Problem setting up the CI/CD settings JavaScript"
-msgstr ""
-
msgid "ProjectSettings|This setting is applied on the server level and can be overridden by an admin."
msgstr ""
@@ -2010,6 +2526,9 @@ msgstr ""
msgid "PrometheusService|View environments"
msgstr ""
+msgid "Protip:"
+msgstr ""
+
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "公開 - 未登入的情æ³ä¸‹ä¾ç„¶å¯ä»¥æŸ¥çœ‹ä»»ä½•å…¬é–‹å°ˆæ¡ˆ"
@@ -2025,6 +2544,9 @@ msgstr "æŽ¨é€ (push) 事件"
msgid "PushRule|Committer restriction"
msgstr ""
+msgid "Quick actions can be used in the issues description and comment boxes."
+msgstr ""
+
msgid "Read more"
msgstr "瞭解更多"
@@ -2037,6 +2559,12 @@ msgstr "分支 (branch) "
msgid "RefSwitcher|Tags"
msgstr "標籤"
+msgid "Reference:"
+msgstr ""
+
+msgid "Register / Sign In"
+msgstr ""
+
msgid "Registry"
msgstr ""
@@ -2061,9 +2589,18 @@ msgstr "相關已åˆä½µçš„請求"
msgid "Remind later"
msgstr "ç¨å¾Œæ醒"
+msgid "Remove"
+msgstr ""
+
+msgid "Remove avatar"
+msgstr ""
+
msgid "Remove project"
msgstr "刪除專案"
+msgid "Repair authentication"
+msgstr ""
+
msgid "Repository"
msgstr "檔案庫 (repository)"
@@ -2079,6 +2616,10 @@ msgstr "é‡ç½®å¥åº·æª¢æŸ¥å­˜å–憑證 (access token)"
msgid "Reset runners registration token"
msgstr "é‡ç½® Runner 註冊憑證 (registration token)"
+msgid "Reveal value"
+msgid_plural "Reveal values"
+msgstr[0] ""
+
msgid "Revert this commit"
msgstr "還原此更動記錄 (commit)"
@@ -2088,15 +2629,15 @@ msgstr "還原此åˆä½µè«‹æ±‚ (merge request) "
msgid "SSH Keys"
msgstr "SSH 金鑰"
-msgid "Save"
-msgstr "儲存"
-
msgid "Save changes"
msgstr "儲存變更"
msgid "Save pipeline schedule"
msgstr "儲存æµæ°´ç·š (pipeline) 排程"
+msgid "Save variables"
+msgstr ""
+
msgid "Schedule a new pipeline"
msgstr "建立æµæ°´ç·š (pipeline) 排程"
@@ -2112,38 +2653,59 @@ msgstr ""
msgid "Search branches and tags"
msgstr "æœå°‹åˆ†æ”¯ (branch) 和標籤"
+msgid "Search milestones"
+msgstr ""
+
+msgid "Search project"
+msgstr ""
+
+msgid "Search users"
+msgstr ""
+
msgid "Seconds before reseting failure information"
msgstr "é‡ç½®å¤±æ•—訊æ¯ç­‰å¾…時間(秒)"
-msgid "Seconds to wait after a storage failure"
-msgstr "儲存失敗後等待時間(秒)"
-
msgid "Seconds to wait for a storage access attempt"
msgstr "等待存å–儲存空間的嘗試時間(秒)"
+msgid "Secret variables"
+msgstr ""
+
msgid "Select Archive Format"
msgstr "é¸æ“‡ä¸‹è¼‰æ ¼å¼"
msgid "Select a timezone"
msgstr "é¸æ“‡æ™‚å€"
+msgid "Select assignee"
+msgstr ""
+
+msgid "Select branch/tag"
+msgstr ""
+
msgid "Select target branch"
msgstr "é¸æ“‡ç›®æ¨™åˆ†æ”¯ (branch) "
+msgid "Selective synchronization"
+msgstr ""
+
msgid "Sep"
msgstr ""
msgid "September"
msgstr ""
+msgid "Server version"
+msgstr ""
+
msgid "Service Templates"
msgstr "æœå‹™ç¯„本"
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "請先設定密碼,æ‰èƒ½ä½¿ç”¨ %{protocol} 來上傳 (push) 或下載 (pull) 。"
-msgid "Set up CI"
-msgstr "設定 CI"
+msgid "Set up CI/CD"
+msgstr ""
msgid "Set up Koding"
msgstr "設定 Koding"
@@ -2157,6 +2719,15 @@ msgstr "設定密碼"
msgid "Settings"
msgstr "設定"
+msgid "SharedRunnersMinutesSettings|By resetting the pipeline minutes for this namespace, the currently used minutes will be set to zero."
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset pipeline minutes"
+msgstr ""
+
+msgid "SharedRunnersMinutesSettings|Reset used pipeline minutes"
+msgstr ""
+
msgid "Show parent pages"
msgstr "顯示上層é é¢"
@@ -2170,9 +2741,6 @@ msgstr[0] "顯示 %d 個事件"
msgid "Sidebar|Change weight"
msgstr ""
-msgid "Sidebar|Edit"
-msgstr ""
-
msgid "Sidebar|No"
msgstr ""
@@ -2185,18 +2753,30 @@ msgstr ""
msgid "Snippets"
msgstr "文字片段"
+msgid "Something went wrong on our end"
+msgstr ""
+
msgid "Something went wrong on our end."
msgstr "發生了錯誤。"
+msgid "Something went wrong trying to change the confidentiality of this issue"
+msgstr ""
+
msgid "Something went wrong trying to change the locked state of this ${this.issuableDisplayName}"
msgstr ""
+msgid "Something went wrong when toggling the button"
+msgstr ""
+
msgid "Something went wrong while fetching the projects."
msgstr "讀å–專案時發生錯誤。"
msgid "Something went wrong while fetching the registry list."
msgstr "讀å–註冊列表時發生錯誤。"
+msgid "Something went wrong. Please try again."
+msgstr ""
+
msgid "Sort by"
msgstr "排åº"
@@ -2326,12 +2906,12 @@ msgstr "å•Ÿå‹• Runner!"
msgid "Stopped"
msgstr ""
+msgid "Storage"
+msgstr ""
+
msgid "Subgroups"
msgstr "å­ç¾¤çµ„"
-msgid "Subscribe"
-msgstr "訂閱"
-
msgid "Switch branch/tag"
msgstr "切æ›åˆ†æ”¯ (branch) 或標籤"
@@ -2426,8 +3006,11 @@ msgstr ""
msgid "The Advanced Global Search in GitLab is a powerful search service that saves you time. Instead of creating duplicate code and wasting time, you can now search for code within other teams that can help your own project."
msgstr ""
-msgid "The circuitbreaker backoff threshold should be lower than the failure count threshold"
-msgstr "é™æµé˜»æ–·å…ƒä»¶çš„觸發門檻應低於計數錯誤門檻"
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
+msgstr ""
+
+msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project. You can register or sign in to create issues for this project."
+msgstr ""
msgid "The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request."
msgstr "程å¼é–‹ç™¼éšŽæ®µé¡¯ç¤ºå¾žç¬¬ä¸€æ¬¡æ›´å‹•è¨˜éŒ„ (commit) 到建立åˆä½µè«‹æ±‚ (merge request) 的時間。建立第一個åˆä½µè«‹æ±‚後,資料將自動填入。"
@@ -2441,21 +3024,18 @@ msgstr "åˆ†æ”¯èˆ‡ä¸»å¹¹é–“çš„é—œè¯ (fork relationship) 已被刪除。"
msgid "The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage."
msgstr "è­°é¡Œ (issue) éšŽæ®µé¡¯ç¤ºå¾žè­°é¡Œå»ºç«‹åˆ°è¨­å®šé‡Œç¨‹ç¢‘æ‰€èŠ±çš„æ™‚é–“ï¼Œæˆ–æ˜¯è­°é¡Œè¢«åˆ†é¡žåˆ°è­°é¡Œçœ‹æ¿ (issue board) 中所花的時間。建立第一個議題後,資料將自動填入。"
+msgid "The maximum file size allowed is 200KB."
+msgstr ""
+
msgid "The number of attempts GitLab will make to access a storage."
msgstr "GitLab å­˜å–儲存空間的嘗試次數。"
-msgid "The number of failures after which GitLab will start temporarily disabling access to a storage shard on a host"
-msgstr ""
-
msgid "The number of failures of after which GitLab will completely prevent access to the storage. The number of failures can be reset in the admin interface: %{link_to_health_page} or using the %{api_documentation_link}."
msgstr "GitLab 將阻擋存å–失敗的次數。在管ç†è€…介é¢ä¸­å¯ä»¥é‡ç½®å¤±æ•—次數: %{link_to_health_page} 或使用 %{api_documentation_link}。"
msgid "The phase of the development lifecycle."
msgstr "專案開發週期的å„個階段。"
-msgid "The pipelines schedule runs pipelines in the future, repeatedly, for specific branches or tags. Those scheduled pipelines will inherit limited project access based on their associated user."
-msgstr "在指定了特定分支 (branch) 或標籤後,此處的æµæ°´ç·š (pipeline) 排程會ä¸æ–·åœ°é‡è¤‡åŸ·è¡Œã€‚æµæ°´ç·šæŽ’程的存å–權é™èˆ‡å°ˆæ¡ˆæœ¬èº«ç›¸åŒã€‚"
-
msgid "The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit."
msgstr "計劃階段顯示從更動記錄 (commit) 被排程至第一個推é€çš„時間。第一次推é€ä¹‹å¾Œï¼Œè³‡æ–™å°‡è‡ªå‹•å¡«å…¥ã€‚"
@@ -2486,20 +3066,47 @@ msgstr "GitLab ä¿å­˜å¤±æ•—訊æ¯çš„時間(秒)。在此時間內若沒有發生
msgid "The time in seconds GitLab will try to access storage. After this time a timeout error will be raised."
msgstr "GitLab 嘗試存å–檔案庫 (repository) 的時間 (秒)。超éŽæ­¤æ™‚間將會引發逾時錯誤。"
+msgid "The time in seconds between storage checks. When a previous check did complete yet, GitLab will skip a check."
+msgstr ""
+
msgid "The time taken by each data entry gathered by that stage."
msgstr "該階段中æ¯ä¸€å€‹è³‡æ–™é …目所花的時間。"
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "中ä½æ•¸æ˜¯ä¸€å€‹æ•¸åˆ—中最中間的值。例如在 3ã€5ã€9 之間,中ä½æ•¸æ˜¯ 5。在 3ã€5ã€7ã€8 之間,中ä½æ•¸æ˜¯ (5 + 7)/ 2 = 6。"
+msgid "There are no issues to show"
+msgstr ""
+
+msgid "There are no merge requests to show"
+msgstr ""
+
msgid "There are problems accessing Git storage: "
msgstr "å­˜å– Git 儲存空間時出ç¾å•é¡Œï¼š"
+msgid "There was an error loading users activity calendar."
+msgstr ""
+
+msgid "There was an error saving your notification settings."
+msgstr ""
+
+msgid "There was an error subscribing to this label."
+msgstr ""
+
+msgid "There was an error when reseting email token."
+msgstr ""
+
+msgid "There was an error when subscribing to this label."
+msgstr ""
+
+msgid "There was an error when unsubscribing from this label."
+msgstr ""
+
msgid "This board\\'s scope is reduced"
msgstr ""
-msgid "This branch has changed since you started editing. Would you like to create a new branch?"
-msgstr "在您編輯後,此分支已被更改,您想è¦å»ºç«‹ä¸€å€‹æ–°çš„分支嗎?"
+msgid "This directory"
+msgstr ""
msgid "This is a confidential issue."
msgstr "這是個隱密å•é¡Œã€‚"
@@ -2507,18 +3114,45 @@ msgstr "這是個隱密å•é¡Œã€‚"
msgid "This is the author's first Merge Request to this project."
msgstr "這是作者第一次åˆä½µè«‹æ±‚至本專案。"
+msgid "This issue is confidential"
+msgstr ""
+
msgid "This issue is confidential and locked."
msgstr "這個å•é¡Œæ˜¯ä¿å¯†ä¸”鎖定的。"
msgid "This issue is locked."
msgstr "這個å•é¡Œå·²è¢«éŽ–定。"
+msgid "This job depends on a user to trigger its process. Often they are used to deploy code to production environments"
+msgstr ""
+
+msgid "This job depends on upstream jobs that need to succeed in order for this job to be triggered"
+msgstr ""
+
+msgid "This job has not been triggered yet"
+msgstr ""
+
+msgid "This job has not started yet"
+msgstr ""
+
+msgid "This job is in pending state and is waiting to be picked by a runner"
+msgstr ""
+
+msgid "This job requires a manual action"
+msgstr ""
+
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "這代表在您建立一個空的檔案庫 (repository) 或是匯入一個ç¾å­˜çš„檔案庫之å‰ï¼Œæ‚¨å°‡ç„¡æ³•ä¸Šå‚³æ›´æ–° (push) 。"
msgid "This merge request is locked."
msgstr "這個åˆä½µè«‹æ±‚已被鎖定。"
+msgid "This project"
+msgstr ""
+
+msgid "This repository"
+msgstr ""
+
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr ""
@@ -2531,9 +3165,21 @@ msgstr "議題 (issue) 等待開始實作的時間"
msgid "Time between merge request creation and merge/close"
msgstr "åˆä½µè«‹æ±‚ (merge request) 從建立到被åˆä½µæˆ–是關閉的時間"
+msgid "Time tracking"
+msgstr ""
+
msgid "Time until first merge request"
msgstr "第一個åˆä½µè«‹æ±‚ (merge request) 被建立å‰çš„時間"
+msgid "TimeTrackingEstimated|Est"
+msgstr ""
+
+msgid "TimeTracking|Estimated:"
+msgstr ""
+
+msgid "TimeTracking|Spent"
+msgstr ""
+
msgid "Timeago|%s days ago"
msgstr " %s 天å‰"
@@ -2671,6 +3317,18 @@ msgstr "秒"
msgid "Title"
msgstr ""
+msgid "Todo"
+msgstr ""
+
+msgid "Toggle sidebar"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: OFF"
+msgstr ""
+
+msgid "ToggleButton|Toggle Status: ON"
+msgstr ""
+
msgid "Total Time"
msgstr "總時間"
@@ -2686,20 +3344,41 @@ msgstr ""
msgid "Track groups of issues that share a theme, across projects and milestones"
msgstr ""
+msgid "Total: %{total}"
+msgstr ""
+
+msgid "Track time with quick actions"
+msgstr ""
+
+msgid "Trigger this manual action"
+msgstr ""
+
msgid "Turn on Service Desk"
msgstr ""
+msgid "Type %{value} to confirm:"
+msgstr ""
+
+msgid "Unable to reset project cache."
+msgstr ""
+
+msgid "Unknown"
+msgstr ""
+
msgid "Unlock"
msgstr "解鎖"
+msgid "Unlock this %{issuableDisplayName}? <strong>Everyone</strong> will be able to comment."
+msgstr ""
+
msgid "Unlocked"
msgstr "已解鎖"
msgid "Unstar"
msgstr "å–消收è—"
-msgid "Unsubscribe"
-msgstr "å–消訂閱"
+msgid "Up to date"
+msgstr ""
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr ""
@@ -2722,6 +3401,9 @@ msgstr "上傳新檔案"
msgid "Upload file"
msgstr "上傳檔案"
+msgid "Upload new avatar"
+msgstr ""
+
msgid "UploadLink|click to upload"
msgstr "點擊上傳"
@@ -2734,9 +3416,15 @@ msgstr "在安è£éŽç¨‹ä¸­ä½¿ç”¨æ­¤è¨»å†Šæ†‘è­‰ (registration token):"
msgid "Use your global notification setting"
msgstr "使用全域通知設定"
+msgid "Variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use variables for passwords, secret keys, or whatever you want."
+msgstr ""
+
msgid "View file @ "
msgstr "ç€è¦½æª”案 @ "
+msgid "View labels"
+msgstr ""
+
msgid "View open merge request"
msgstr "查看此分支的åˆä½µè«‹æ±‚ (merge request)"
@@ -2758,6 +3446,9 @@ msgstr "ä¸æ˜Ž"
msgid "Want to see the data? Please ask an administrator for access."
msgstr "權é™ä¸è¶³ã€‚如需查看相關資料,請å‘管ç†å“¡ç”³è«‹æ¬Šé™ã€‚"
+msgid "We could not verify that one of your projects on GCP has billing enabled. Please try again."
+msgstr ""
+
msgid "We don't have enough data to show this stage."
msgstr "因該階段的資料ä¸è¶³è€Œç„¡æ³•é¡¯ç¤ºç›¸é—œè³‡è¨Š"
@@ -2770,9 +3461,6 @@ msgstr ""
msgid "Weight"
msgstr ""
-msgid "When access to a storage fails. GitLab will prevent access to the storage for the time specified here. This allows the filesystem to recover. Repositories on failing shards are temporarly unavailable"
-msgstr "當存å–檔案庫 (repository) 失敗時, GitLab 將在此處指定的時間內防止檔案庫的存å–,以此等待檔案系統æ¢å¾©ã€‚å¤±æ•—çš„æª”æ¡ˆåº«åˆ†æµ (shard) 會暫時無法使用。"
-
msgid "Wiki"
msgstr "Wiki"
@@ -2791,6 +3479,12 @@ msgstr "å®ƒè¢«æŽ¨è–¦å®‰è£ %{markdown} 所以那 GFM 功能在本機呈ç¾ï¼š"
msgid "WikiClone|Start Gollum and edit locally"
msgstr "開始你的 Gollum 並在本機編輯。"
+msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
+msgstr ""
+
+msgid "WikiEdit|There is already a page with the same title in that path."
+msgstr ""
+
msgid "WikiEmptyPageError|You are not allowed to create wiki pages"
msgstr "你沒有權é™å»ºç«‹ Wiki é é¢"
@@ -2893,9 +3587,21 @@ msgstr "å°‡è¦åˆªé™¤æœ¬åˆ†æ”¯å°ˆæ¡ˆèˆ‡ä¸»å¹¹ %{forked_from_project} 的所有關
msgid "You are going to transfer %{project_name_with_namespace} to another owner. Are you ABSOLUTELY sure?"
msgstr "å°‡è¦æŠŠ %{project_name_with_namespace} 的所有權轉移給å¦ä¸€å€‹äººã€‚真的「確定ã€è¦é€™éº¼åšå—Žï¼Ÿ"
+msgid "You can also star a label to make it a priority label."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
+msgid "You can move around the graph by using the arrow keys."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ (branch) 上建立檔案"
+msgid "You can only edit files when you are on a branch"
+msgstr ""
+
msgid "You cannot write to a read-only secondary GitLab Geo instance. Please use %{link_to_primary_node} instead."
msgstr ""
@@ -2935,6 +3641,12 @@ msgstr "在個人帳號中 %{add_ssh_key_link} 之å‰ï¼Œ 將無法使用 SSH 上
msgid "You won't be able to pull or push project code via SSH until you add an SSH key to your profile"
msgstr ""
+msgid "You'll need to use different branch names to get a valid comparison."
+msgstr ""
+
+msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr "你的留言將ä¸æœƒè¢«å…¬é–‹ã€‚"
@@ -2947,25 +3659,218 @@ msgstr "您的åå­—"
msgid "Your projects"
msgstr "你的計劃"
+msgid "assign yourself"
+msgstr ""
+
msgid "branch name"
msgstr ""
msgid "by"
msgstr ""
+msgid "ciReport|Code quality"
+msgstr ""
+
+msgid "ciReport|DAST detected no alerts by analyzing the review app"
+msgstr ""
+
+msgid "ciReport|Failed to load ${type} report"
+msgstr ""
+
+msgid "ciReport|Fixed:"
+msgstr ""
+
+msgid "ciReport|Instances"
+msgstr ""
+
+msgid "ciReport|Learn more about whitelisting"
+msgstr ""
+
+msgid "ciReport|Loading ${type} report"
+msgstr ""
+
+msgid "ciReport|No changes to code quality"
+msgstr ""
+
+msgid "ciReport|No changes to performance metrics"
+msgstr ""
+
+msgid "ciReport|Performance metrics"
+msgstr ""
+
+msgid "ciReport|SAST"
+msgstr ""
+
+msgid "ciReport|SAST detected no security vulnerabilities"
+msgstr ""
+
+msgid "ciReport|SAST:container no vulnerabilities were found"
+msgstr ""
+
+msgid "ciReport|Show complete code vulnerabilities report"
+msgstr ""
+
+msgid "ciReport|Unapproved vulnerabilities (red) can be marked as approved. %{helpLink}"
+msgstr ""
+
msgid "commit"
msgstr ""
+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 ""
+
+msgid "confidentiality|You are going to turn on the confidentiality. This means that only team members with <strong>at least Reporter access</strong> are able to see and leave comments on the issue."
+msgstr ""
+
msgid "day"
msgid_plural "days"
msgstr[0] "天"
+msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
+msgstr ""
+
+msgid "merge request"
+msgid_plural "merge requests"
+msgstr[0] ""
+
+msgid "mrWidget|Cancel automatic merge"
+msgstr ""
+
+msgid "mrWidget|Check out branch"
+msgstr ""
+
+msgid "mrWidget|Checking ability to merge automatically"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick"
+msgstr ""
+
+msgid "mrWidget|Cherry-pick this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Closed"
+msgstr ""
+
+msgid "mrWidget|Closed by"
+msgstr ""
+
+msgid "mrWidget|Closes"
+msgstr ""
+
+msgid "mrWidget|Did not close"
+msgstr ""
+
+msgid "mrWidget|Email patches"
+msgstr ""
+
+msgid "mrWidget|If the %{branch} branch exists in your local repository, you can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|Mentions"
+msgstr ""
+
+msgid "mrWidget|Merge"
+msgstr ""
+
+msgid "mrWidget|Merge failed."
+msgstr ""
+
+msgid "mrWidget|Merge locally"
+msgstr ""
+
+msgid "mrWidget|Merged by"
+msgstr ""
+
+msgid "mrWidget|Plain diff"
+msgstr ""
+
+msgid "mrWidget|Refresh"
+msgstr ""
+
+msgid "mrWidget|Refresh now"
+msgstr ""
+
+msgid "mrWidget|Refreshing now"
+msgstr ""
+
+msgid "mrWidget|Remove Source Branch"
+msgstr ""
+
+msgid "mrWidget|Remove source branch"
+msgstr ""
+
+msgid "mrWidget|Request to merge"
+msgstr ""
+
+msgid "mrWidget|Resolve conflicts"
+msgstr ""
+
+msgid "mrWidget|Revert"
+msgstr ""
+
+msgid "mrWidget|Revert this merge request in a new merge request"
+msgstr ""
+
+msgid "mrWidget|Set by"
+msgstr ""
+
+msgid "mrWidget|The changes were merged into"
+msgstr ""
+
+msgid "mrWidget|The changes were not merged into"
+msgstr ""
+
+msgid "mrWidget|The changes will be merged into"
+msgstr ""
+
+msgid "mrWidget|The source branch has been removed"
+msgstr ""
+
+msgid "mrWidget|The source branch is being removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will be removed"
+msgstr ""
+
+msgid "mrWidget|The source branch will not be removed"
+msgstr ""
+
+msgid "mrWidget|There are merge conflicts"
+msgstr ""
+
+msgid "mrWidget|This merge request failed to be merged automatically"
+msgstr ""
+
+msgid "mrWidget|This merge request is in the process of being merged"
+msgstr ""
+
+msgid "mrWidget|This project is archived, write access has been disabled"
+msgstr ""
+
+msgid "mrWidget|You can merge this merge request manually using the"
+msgstr ""
+
+msgid "mrWidget|You can remove source branch now"
+msgstr ""
+
+msgid "mrWidget|command line"
+msgstr ""
+
+msgid "mrWidget|into"
+msgstr ""
+
+msgid "mrWidget|to be merged automatically when the pipeline succeeds"
+msgstr ""
+
msgid "new merge request"
msgstr "建立åˆä½µè«‹æ±‚"
msgid "notification emails"
msgstr "通知信"
+msgid "or"
+msgstr ""
+
msgid "parent"
msgid_plural "parents"
msgstr[0] "上層"
@@ -2976,12 +3881,21 @@ msgstr "密碼"
msgid "personal access token"
msgstr "ç§äººå­˜å–憑證 (access token)"
+msgid "remove due date"
+msgstr ""
+
msgid "source"
msgstr ""
+msgid "spendCommand|%{slash_command} will update the sum of the time spent."
+msgstr ""
+
msgid "to help your contributors communicate effectively!"
msgstr ""
msgid "username"
msgstr "使用者å稱"
+msgid "uses Kubernetes clusters to deploy your code!"
+msgstr ""
+
diff --git a/package.json b/package.json
index c508a6e9931..043af80a3be 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,6 @@
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-core": "^6.26.0",
- "babel-eslint": "^8.0.2",
"babel-loader": "^7.1.2",
"babel-plugin-transform-define": "^1.3.0",
"babel-preset-latest": "^6.24.1",
@@ -24,13 +23,14 @@
"blackst0ne-mermaid": "^7.1.0-fixed",
"bootstrap-sass": "^3.3.6",
"brace-expansion": "^1.1.8",
+ "chart.js": "1.0.2",
"classlist-polyfill": "^1.2.0",
"clipboard": "^1.7.1",
- "compression-webpack-plugin": "^1.0.0",
- "copy-webpack-plugin": "^4.0.1",
+ "compression-webpack-plugin": "^1.1.7",
+ "copy-webpack-plugin": "^4.4.1",
"core-js": "^2.4.1",
"cropper": "^2.3.0",
- "css-loader": "^0.28.0",
+ "css-loader": "^0.28.9",
"d3-array": "^1.2.1",
"d3-axis": "^1.0.8",
"d3-brush": "^1.0.4",
@@ -44,17 +44,19 @@
"document-register-element": "1.3.0",
"dropzone": "^4.2.0",
"emoji-unicode-version": "^0.2.1",
- "exports-loader": "^0.6.4",
- "file-loader": "^0.11.1",
+ "exports-loader": "^0.7.0",
+ "file-loader": "^1.1.8",
"fuzzaldrin-plus": "^0.5.0",
"glob": "^7.1.2",
- "imports-loader": "^0.7.1",
+ "imports-loader": "^0.8.0",
"jed": "^1.1.1",
- "jquery": "^2.2.4",
+ "jquery": "^3.2.1",
"jquery-ujs": "1.2.2",
+ "jquery.waitforimages": "^2.2.0",
"js-cookie": "^2.1.3",
"jszip": "^3.1.3",
"jszip-utils": "^0.0.2",
+ "katex": "^0.8.3",
"marked": "^0.3.12",
"monaco-editor": "0.10.0",
"mousetrap": "^1.4.6",
@@ -64,32 +66,34 @@
"raphael": "^2.2.7",
"raven-js": "^3.22.1",
"raw-loader": "^0.5.1",
- "react-dev-utils": "^0.5.2",
+ "react-dev-utils": "^5.0.0",
"sanitize-html": "^1.16.1",
"select2": "3.5.2-browserify",
"sql.js": "^0.4.0",
+ "style-loader": "^0.20.2",
"svg4everybody": "2.1.9",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
"three-stl-loader": "^1.0.4",
"timeago.js": "^3.0.2",
"underscore": "^1.8.3",
- "url-loader": "^0.5.8",
+ "url-loader": "^0.6.2",
"visibilityjs": "^1.2.4",
"vue": "^2.5.13",
- "vue-loader": "^13.7.0",
+ "vue-loader": "^14.1.1",
"vue-resource": "^1.3.5",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.5.13",
"vuex": "^3.0.1",
- "webpack": "^3.5.5",
- "webpack-bundle-analyzer": "^2.8.2",
+ "webpack": "^3.11.0",
+ "webpack-bundle-analyzer": "^2.10.0",
"webpack-stats-plugin": "^0.1.5",
"worker-loader": "^1.1.0"
},
"devDependencies": {
"@gitlab-org/gitlab-svgs": "^1.8.0",
"axios-mock-adapter": "^1.10.0",
+ "babel-eslint": "^8.0.2",
"babel-plugin-istanbul": "^4.1.5",
"eslint": "^3.18.0",
"eslint-config-airbnb-base": "^10.0.1",
@@ -105,13 +109,13 @@
"jasmine-jquery": "^2.1.1",
"karma": "^2.0.0",
"karma-chrome-launcher": "^2.2.0",
- "karma-coverage-istanbul-reporter": "^1.3.3",
+ "karma-coverage-istanbul-reporter": "^1.4.1",
"karma-jasmine": "^1.1.1",
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "2.0.7",
- "nodemon": "^1.11.0",
+ "nodemon": "^1.15.1",
"prettier": "1.9.2",
- "webpack-dev-server": "^2.6.1"
+ "webpack-dev-server": "^2.11.1"
}
}
diff --git a/qa/README.md b/qa/README.md
index 3c1b61900d9..3a99a30d379 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -70,6 +70,30 @@ If you need to authenticate as a different user, you can provide the
GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password bin/qa Test::Instance https://gitlab.example.com
```
+If your user doesn't have permission to default sandbox group
+`gitlab-qa-sandbox`, you could also use another sandbox group by giving
+`GITLAB_SANDBOX_NAME`:
+
+```
+GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance https://gitlab.example.com
+```
+
+In addition, the `GITLAB_USER_TYPE` can be set to "ldap" to sign in as an LDAP user:
+
+```
+GITLAB_USER_TYPE=ldap GITLAB_USERNAME=jsmith GITLAB_PASSWORD=password GITLAB_SANDBOX_NAME=jsmith-qa-sandbox bin/qa Test::Instance https://gitlab.example.com
+```
+
All [supported environment variables are here](https://gitlab.com/gitlab-org/gitlab-qa#supported-environment-variables).
+### Building a Docker image to test
+
+Once you have made changes to the CE/EE repositories, you may want to build a
+Docker image to test locally instead of waiting for the `gitlab-ce-qa` or
+`gitlab-ee-qa` nightly builds. To do that, you can run from this directory:
+
+```sh
+docker build -t gitlab/gitlab-ce-qa:nightly .
+```
+
[GDK]: https://gitlab.com/gitlab-org/gitlab-development-kit/
diff --git a/qa/qa.rb b/qa/qa.rb
index 3c7778a4260..7220af5088e 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -117,6 +117,10 @@ module QA
autoload :Show, 'qa/page/project/pipeline/show'
end
+ module Job
+ autoload :Show, 'qa/page/project/job/show'
+ end
+
module Settings
autoload :Common, 'qa/page/project/settings/common'
autoload :Advanced, 'qa/page/project/settings/advanced'
@@ -126,6 +130,7 @@ module QA
autoload :DeployKeys, 'qa/page/project/settings/deploy_keys'
autoload :SecretVariables, 'qa/page/project/settings/secret_variables'
autoload :Runners, 'qa/page/project/settings/runners'
+ autoload :MergeRequest, 'qa/page/project/settings/merge_request'
end
module Issue
@@ -141,6 +146,7 @@ module QA
module MergeRequest
autoload :New, 'qa/page/merge_request/new'
+ autoload :Show, 'qa/page/merge_request/show'
end
module Admin
@@ -165,6 +171,7 @@ module QA
#
module Git
autoload :Repository, 'qa/git/repository'
+ autoload :Location, 'qa/git/location'
end
##
diff --git a/qa/qa/factory/base.rb b/qa/qa/factory/base.rb
index bd66b74a164..afaa96b4541 100644
--- a/qa/qa/factory/base.rb
+++ b/qa/qa/factory/base.rb
@@ -22,7 +22,7 @@ module QA
factory.fabricate!(*args)
- return Factory::Product.populate!(self)
+ return Factory::Product.populate!(factory)
end
end
diff --git a/qa/qa/factory/product.rb b/qa/qa/factory/product.rb
index d004e642f9b..996b7f14f61 100644
--- a/qa/qa/factory/product.rb
+++ b/qa/qa/factory/product.rb
@@ -17,8 +17,9 @@ module QA
def self.populate!(factory)
new.tap do |product|
- factory.attributes.each_value do |attribute|
- product.instance_exec(&attribute.block).tap do |value|
+ factory.class.attributes.each_value do |attribute|
+ product.instance_exec(factory, attribute.block) do |factory, block|
+ value = block.call(factory)
product.define_singleton_method(attribute.name) { value }
end
end
diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb
index 2f4de4173d4..6e8905cde78 100644
--- a/qa/qa/factory/repository/push.rb
+++ b/qa/qa/factory/repository/push.rb
@@ -2,7 +2,7 @@ module QA
module Factory
module Repository
class Push < Factory::Base
- attr_writer :file_name, :file_content, :commit_message, :branch_name
+ attr_writer :file_name, :file_content, :commit_message, :branch_name, :new_branch
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-code'
@@ -14,6 +14,7 @@ module QA
@file_content = '# This is test project'
@commit_message = "Add #{@file_name}"
@branch_name = 'master'
+ @new_branch = true
end
def fabricate!
@@ -29,6 +30,7 @@ module QA
repository.clone
repository.configure_identity('GitLab QA', 'root@gitlab.com')
+ repository.checkout(@branch_name) unless @new_branch
repository.add_file(@file_name, @file_content)
repository.commit(@commit_message)
repository.push_changes(@branch_name)
diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb
index ce04e904aaf..539fe6b8a70 100644
--- a/qa/qa/factory/resource/merge_request.rb
+++ b/qa/qa/factory/resource/merge_request.rb
@@ -9,11 +9,20 @@ module QA
:source_branch,
:target_branch
+ product :project do |factory|
+ factory.project
+ end
+
+ product :source_branch do |factory|
+ factory.source_branch
+ end
+
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-merge-request'
end
dependency Factory::Repository::Push, as: :target do |push, factory|
+ factory.project.visit!
push.project = factory.project
push.branch_name = "master:#{factory.target_branch}"
end
diff --git a/qa/qa/factory/resource/runner.rb b/qa/qa/factory/resource/runner.rb
index 5f37f8ac2e9..03b69eb1bdf 100644
--- a/qa/qa/factory/resource/runner.rb
+++ b/qa/qa/factory/resource/runner.rb
@@ -4,7 +4,7 @@ module QA
module Factory
module Resource
class Runner < Factory::Base
- attr_writer :name, :tags
+ attr_writer :name, :tags, :image
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-ci-cd'
@@ -19,6 +19,10 @@ module QA
@tags || %w[qa e2e]
end
+ def image
+ @image || 'gitlab/gitlab-runner:alpine'
+ end
+
def fabricate!
project.visit!
@@ -31,6 +35,7 @@ module QA
runner.token = runners.registration_token
runner.address = runners.coordinator_address
runner.tags = tags
+ runner.image = image
runner.register!
end
end
diff --git a/qa/qa/factory/resource/secret_variable.rb b/qa/qa/factory/resource/secret_variable.rb
index af0fa8af2df..c734d739b4a 100644
--- a/qa/qa/factory/resource/secret_variable.rb
+++ b/qa/qa/factory/resource/secret_variable.rb
@@ -4,18 +4,6 @@ module QA
class SecretVariable < Factory::Base
attr_accessor :key, :value
- product :key do
- Page::Project::Settings::CICD.act do
- expand_secret_variables(&:variable_key)
- end
- end
-
- product :value do
- Page::Project::Settings::CICD.act do
- expand_secret_variables(&:variable_value)
- end
- end
-
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-with-secret-variables'
project.description = 'project for adding secret variable test'
diff --git a/qa/qa/git/location.rb b/qa/qa/git/location.rb
new file mode 100644
index 00000000000..30538388530
--- /dev/null
+++ b/qa/qa/git/location.rb
@@ -0,0 +1,32 @@
+require 'uri'
+require 'forwardable'
+
+module QA
+ module Git
+ class Location
+ extend Forwardable
+
+ attr_reader :git_uri, :uri
+ def_delegators :@uri, :user, :host, :path
+
+ # See: config/initializers/1_settings.rb
+ # Settings#build_gitlab_shell_ssh_path_prefix
+ def initialize(git_uri)
+ @git_uri = git_uri
+ @uri =
+ if git_uri.start_with?('ssh://')
+ URI.parse(git_uri)
+ else
+ *rest, path = git_uri.split(':')
+ # Host cannot have : so we'll need to escape it
+ user_host = rest.join('%3A').sub(/\A\[(.+)\]\z/, '\1')
+ URI.parse("ssh://#{user_host}/#{path}")
+ end
+ end
+
+ def port
+ uri.port || 22
+ end
+ end
+ end
+end
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 8f999511d58..b3150e8f3fa 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -1,3 +1,4 @@
+require 'cgi'
require 'uri'
module QA
@@ -32,7 +33,11 @@ module QA
end
def clone(opts = '')
- `git clone #{opts} #{@uri.to_s} ./`
+ `git clone #{opts} #{@uri.to_s} ./ #{suppress_output}`
+ end
+
+ def checkout(branch_name)
+ `git checkout "#{branch_name}"`
end
def shallow_clone
@@ -60,12 +65,22 @@ module QA
end
def push_changes(branch = 'master')
- `git push #{@uri.to_s} #{branch}`
+ `git push #{@uri.to_s} #{branch} #{suppress_output}`
end
def commits
`git log --oneline`.split("\n")
end
+
+ private
+
+ def suppress_output
+ # If we're running as the default user, it's probably a temporary
+ # instance and output can be useful for debugging
+ return if @username == Runtime::User.default_name
+
+ "&> #{File::NULL}"
+ end
end
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 5c3af4b9115..a313d46205d 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -17,7 +17,8 @@ module QA
start = Time.now
while Time.now - start < max
- return true if yield
+ result = yield
+ return result if result
sleep(time)
@@ -97,6 +98,10 @@ module QA
views.map(&:errors).flatten
end
+ def self.elements
+ views.map(&:elements).flatten
+ end
+
class DSL
attr_reader :views
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 0d1ffd9694a..596205fe540 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -31,22 +31,37 @@ module QA
end
end
+ def set_initial_password_if_present
+ if page.has_content?('Change your password')
+ fill_in :user_password, with: Runtime::User.password
+ fill_in :user_password_confirmation, with: Runtime::User.password
+ click_button 'Change your password'
+ end
+ end
+
+ def sign_in_using_credentials
+ if Runtime::User.ldap_user?
+ sign_in_using_ldap_credentials
+ else
+ sign_in_using_gitlab_credentials
+ end
+ end
+
def sign_in_using_ldap_credentials
- click_link 'LDAP'
+ using_wait_time 0 do
+ set_initial_password_if_present
- fill_in :username, with: Runtime::User.name
- fill_in :password, with: Runtime::User.password
+ click_link 'LDAP'
- click_button 'Sign in'
+ fill_in :username, with: Runtime::User.ldap_username
+ fill_in :password, with: Runtime::User.ldap_password
+ click_button 'Sign in'
+ end
end
- def sign_in_using_credentials
+ def sign_in_using_gitlab_credentials
using_wait_time 0 do
- if page.has_content?('Change your password')
- fill_in :user_password, with: Runtime::User.password
- fill_in :user_password_confirmation, with: Runtime::User.password
- click_button 'Change your password'
- end
+ set_initial_password_if_present
click_link 'Standard' if page.has_content?('LDAP')
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
new file mode 100644
index 00000000000..35875487da8
--- /dev/null
+++ b/qa/qa/page/merge_request/show.rb
@@ -0,0 +1,46 @@
+module QA
+ module Page
+ module MergeRequest
+ class Show < Page::Base
+ view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_ready_to_merge.js' do
+ element :merge_button
+ end
+
+ view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue' do
+ element :merged_status, 'The changes were merged into'
+ end
+
+ view 'app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue' do
+ element :mr_rebase_button
+ element :fast_forward_nessage, "Fast-forward merge is not possible"
+ end
+
+ def rebase!
+ wait(reload: false) do
+ click_element :mr_rebase_button
+
+ has_text?("The source branch HEAD has recently changed.")
+ end
+ end
+
+ def fast_forward_possible?
+ !has_text?("Fast-forward merge is not possible")
+ end
+
+ def has_merge_button?
+ refresh
+
+ has_selector?('.accept-merge-request')
+ end
+
+ def merge!
+ wait(reload: false) do
+ click_element :merge_button
+
+ has_text?("The changes were merged into")
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
new file mode 100644
index 00000000000..21bda74efb2
--- /dev/null
+++ b/qa/qa/page/project/job/show.rb
@@ -0,0 +1,19 @@
+module QA::Page
+ module Project::Job
+ class Show < QA::Page::Base
+ view 'app/views/projects/jobs/show.html.haml' do
+ element :build_output, '.js-build-output'
+ end
+
+ def output
+ css = '.js-build-output'
+
+ wait(reload: false) do
+ has_css?(css)
+ end
+
+ find(css).text
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/pipeline/index.rb b/qa/qa/page/project/pipeline/index.rb
index 32c108393b9..ce430a2a6ee 100644
--- a/qa/qa/page/project/pipeline/index.rb
+++ b/qa/qa/page/project/pipeline/index.rb
@@ -6,7 +6,13 @@ module QA::Page
end
def go_to_latest_pipeline
- first('.js-pipeline-url-link').click
+ css = '.js-pipeline-url-link'
+
+ link = wait(reload: false) do
+ first(css)
+ end
+
+ link.click
end
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index 0835173f1cd..b183552d46c 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -11,6 +11,7 @@ module QA::Page
view 'app/assets/javascripts/pipelines/components/graph/job_component.vue' do
element :job_component, /class.*ci-job-component.*/
+ element :job_link, /class.*js-pipeline-graph-job-link.*/
end
view 'app/assets/javascripts/vue_shared/components/ci_icon.vue' do
@@ -30,6 +31,16 @@ module QA::Page
end
end
end
+
+ def go_to_first_job
+ css = '.js-pipeline-graph-job-link'
+
+ wait(reload: false) do
+ has_css?(css)
+ end
+
+ first(css).click
+ end
end
end
end
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
new file mode 100644
index 00000000000..b147c91b467
--- /dev/null
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -0,0 +1,27 @@
+module QA
+ module Page
+ module Project
+ module Settings
+ class MergeRequest < QA::Page::Base
+ include Common
+
+ view 'app/views/projects/_merge_request_fast_forward_settings.html.haml' do
+ element :radio_button_merge_ff
+ end
+
+ view 'app/views/projects/edit.html.haml' do
+ element :merge_request_settings, 'Merge request settings'
+ element :save_merge_request_changes
+ end
+
+ def enable_ff_only
+ expand_section('Merge request settings') do
+ click_element :radio_button_merge_ff
+ click_element :save_merge_request_changes
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/settings/secret_variables.rb b/qa/qa/page/project/settings/secret_variables.rb
index fea4acb389a..c95c79f137d 100644
--- a/qa/qa/page/project/settings/secret_variables.rb
+++ b/qa/qa/page/project/settings/secret_variables.rb
@@ -6,39 +6,37 @@ module QA
include Common
view 'app/views/ci/variables/_variable_row.html.haml' do
+ element :variable_row, '.ci-variable-row-body'
element :variable_key, '.js-ci-variable-input-key'
element :variable_value, '.js-ci-variable-input-value'
+ element :key_placeholder, 'Input variable key'
+ element :value_placeholder, 'Input variable value'
end
view 'app/views/ci/variables/_index.html.haml' do
element :save_variables, '.js-secret-variables-save-button'
+ element :reveal_values, '.js-secret-value-reveal-button'
end
def fill_variable_key(key)
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
- page.find('.js-ci-variable-input-key').set(key)
- end
+ fill_in('Input variable key', with: key, match: :first)
end
def fill_variable_value(value)
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
- page.find('.js-ci-variable-input-value').set(value)
- end
+ fill_in('Input variable value', with: value, match: :first)
end
def save_variables
- click_button('Save variables')
+ find('.js-secret-variables-save-button').click
end
- def variable_key
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
- page.find('.js-ci-variable-input-key').value
- end
+ def reveal_variables
+ find('.js-secret-value-reveal-button').click
end
- def variable_value
- page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
- page.find('.js-ci-variable-input-value').value
+ def variable_value(key)
+ within('.ci-variable-row-body', text: key) do
+ find('.js-ci-variable-input-value').value
end
end
end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 9d2a84ea644..0c7ad46d36b 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -22,27 +22,33 @@ module QA
end
def choose_repository_clone_http
- wait(reload: false) do
- click_element :clone_dropdown
-
- page.within('.clone-options-dropdown') do
- click_link('HTTP')
- end
+ choose_repository_clone('HTTP', 'http')
+ end
- # Ensure git clone textbox was updated to http URI
- repository_location.include?('http')
- end
+ def choose_repository_clone_ssh
+ # It's not always beginning with ssh:// so detecting with @
+ # would be more reliable because ssh would always contain it.
+ # We can't use .git because HTTP also contain that part.
+ choose_repository_clone('SSH', '@')
end
def repository_location
find('#project_clone').value
end
+ def repository_location_uri
+ Git::Location.new(repository_location)
+ end
+
def project_name
find('.qa-project-name').text
end
def new_merge_request
+ wait(reload: true) do
+ has_css?(element_selector_css(:create_merge_request))
+ end
+
click_element :create_merge_request
end
@@ -56,6 +62,21 @@ module QA
click_link 'New issue'
end
+
+ private
+
+ def choose_repository_clone(kind, detect_text)
+ wait(reload: false) do
+ click_element :clone_dropdown
+
+ page.within('.clone-options-dropdown') do
+ click_link(kind)
+ end
+
+ # Ensure git clone textbox was updated
+ repository_location.include?(detect_text)
+ end
+ end
end
end
end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 56944e8b641..fe432edfa2a 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -16,6 +16,36 @@ module QA
def personal_access_token
ENV['PERSONAL_ACCESS_TOKEN']
end
+
+ # By default, "standard" denotes a standard GitLab user login.
+ # Set this to "ldap" if the user should be logged in via LDAP.
+ def user_type
+ (ENV['GITLAB_USER_TYPE'] || 'standard').tap do |type|
+ unless %w(ldap standard).include?(type)
+ raise ArgumentError.new("Invalid user type '#{type}': must be 'ldap' or 'standard'")
+ end
+ end
+ end
+
+ def user_username
+ ENV['GITLAB_USERNAME']
+ end
+
+ def user_password
+ ENV['GITLAB_PASSWORD']
+ end
+
+ def ldap_username
+ ENV['GITLAB_LDAP_USERNAME']
+ end
+
+ def ldap_password
+ ENV['GITLAB_LDAP_PASSWORD']
+ end
+
+ def sandbox_name
+ ENV['GITLAB_SANDBOX_NAME']
+ end
end
end
end
diff --git a/qa/qa/runtime/namespace.rb b/qa/qa/runtime/namespace.rb
index a72c2d21898..8d05b387416 100644
--- a/qa/qa/runtime/namespace.rb
+++ b/qa/qa/runtime/namespace.rb
@@ -16,7 +16,7 @@ module QA
end
def sandbox_name
- 'gitlab-qa-sandbox'
+ Runtime::Env.sandbox_name || 'gitlab-qa-sandbox'
end
end
end
diff --git a/qa/qa/runtime/rsa_key.rb b/qa/qa/runtime/rsa_key.rb
index d456062bce7..fcd7dcc4f02 100644
--- a/qa/qa/runtime/rsa_key.rb
+++ b/qa/qa/runtime/rsa_key.rb
@@ -7,7 +7,7 @@ module QA
extend Forwardable
attr_reader :key
- def_delegators :@key, :fingerprint
+ def_delegators :@key, :fingerprint, :to_pem
def initialize(bits = 4096)
@key = OpenSSL::PKey::RSA.new(bits)
diff --git a/qa/qa/runtime/user.rb b/qa/qa/runtime/user.rb
index 60027c89ab1..c80ee6d4d96 100644
--- a/qa/qa/runtime/user.rb
+++ b/qa/qa/runtime/user.rb
@@ -3,12 +3,28 @@ module QA
module User
extend self
+ def default_name
+ 'root'
+ end
+
def name
- ENV['GITLAB_USERNAME'] || 'root'
+ Runtime::Env.user_username || default_name
end
def password
- ENV['GITLAB_PASSWORD'] || '5iveL!fe'
+ Runtime::Env.user_password || '5iveL!fe'
+ end
+
+ def ldap_user?
+ Runtime::Env.user_type == 'ldap'
+ end
+
+ def ldap_username
+ Runtime::Env.ldap_username || name
+ end
+
+ def ldap_password
+ Runtime::Env.ldap_password || password
end
end
end
diff --git a/qa/qa/service/runner.rb b/qa/qa/service/runner.rb
index d0ee33c69f2..c0352e0467a 100644
--- a/qa/qa/service/runner.rb
+++ b/qa/qa/service/runner.rb
@@ -15,6 +15,14 @@ module QA
@tags = %w[qa test]
end
+ def network
+ shell "docker network inspect #{@network}"
+ rescue CommandError
+ 'bridge'
+ else
+ @network
+ end
+
def pull
shell "docker pull #{@image}"
end
@@ -22,7 +30,7 @@ module QA
def register!
shell <<~CMD.tr("\n", ' ')
docker run -d --rm --entrypoint=/bin/sh
- --network #{@network} --name #{@name}
+ --network #{network} --name #{@name}
-e CI_SERVER_URL=#{@address}
-e REGISTER_NON_INTERACTIVE=true
-e REGISTRATION_TOKEN=#{@token}
diff --git a/qa/qa/service/shellout.rb b/qa/qa/service/shellout.rb
index 898febde63c..76fb2af6319 100644
--- a/qa/qa/service/shellout.rb
+++ b/qa/qa/service/shellout.rb
@@ -3,6 +3,8 @@ require 'open3'
module QA
module Service
module Shellout
+ CommandError = Class.new(StandardError)
+
##
# TODO, make it possible to use generic QA framework classes
# as a library - gitlab-org/gitlab-qa#94
@@ -14,7 +16,7 @@ module QA
out.each { |line| puts line }
if wait.value.exited? && wait.value.exitstatus.nonzero?
- raise "Command `#{command}` failed!"
+ raise CommandError, "Command `#{command}` failed!"
end
end
end
diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb
index 9d039590a0e..d4ff4ebbc9a 100644
--- a/qa/qa/specs/features/api/users_spec.rb
+++ b/qa/qa/specs/features/api/users_spec.rb
@@ -14,7 +14,7 @@ module QA
end
scenario 'submit request with a valid user name' do
- get request.url, { params: { username: 'root' } }
+ get request.url, { params: { username: Runtime::User.name } }
expect_status(200)
expect(json_body).to be_an Array
@@ -23,7 +23,7 @@ module QA
end
scenario 'submit request with an invalid user name' do
- get request.url, { params: { username: 'invalid' } }
+ get request.url, { params: { username: SecureRandom.hex(10) } }
expect_status(200)
expect(json_body).to be_an Array
diff --git a/qa/qa/specs/features/merge_request/rebase_spec.rb b/qa/qa/specs/features/merge_request/rebase_spec.rb
new file mode 100644
index 00000000000..2a44d42af6f
--- /dev/null
+++ b/qa/qa/specs/features/merge_request/rebase_spec.rb
@@ -0,0 +1,39 @@
+module QA
+ feature 'merge request rebase', :core do
+ scenario 'rebases source branch of merge request' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ project = Factory::Resource::Project.fabricate! do |project|
+ project.name = "only-fast-forward"
+ end
+
+ Page::Menu::Side.act { go_to_settings }
+ Page::Project::Settings::MergeRequest.act { enable_ff_only }
+
+ merge_request = Factory::Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.project = project
+ merge_request.title = 'Needs rebasing'
+ end
+
+ Factory::Repository::Push.fabricate! do |push|
+ push.project = project
+ push.file_name = "other.txt"
+ push.file_content = "New file added!"
+ end
+
+ merge_request.visit!
+
+ Page::MergeRequest::Show.perform do |merge_request|
+ expect(merge_request).to have_content('Needs rebasing')
+ expect(merge_request).not_to be_fast_forward_possible
+ expect(merge_request).not_to have_merge_button
+
+ merge_request.rebase!
+
+ expect(merge_request).to have_merge_button
+ expect(merge_request.fast_forward_possible?).to be_truthy
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/add_secret_variable_spec.rb b/qa/qa/specs/features/project/add_secret_variable_spec.rb
index 36422a92afc..d1bf7849bd0 100644
--- a/qa/qa/specs/features/project/add_secret_variable_spec.rb
+++ b/qa/qa/specs/features/project/add_secret_variable_spec.rb
@@ -4,16 +4,21 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
- variable_key = 'VARIABLE_KEY'
- variable_value = 'variable value'
-
- variable = Factory::Resource::SecretVariable.fabricate! do |resource|
- resource.key = variable_key
- resource.value = variable_value
+ Factory::Resource::SecretVariable.fabricate! do |resource|
+ resource.key = 'VARIABLE_KEY'
+ resource.value = 'some secret variable'
end
- expect(variable.key).to eq(variable_key)
- expect(variable.value).to eq(variable_value)
+ Page::Project::Settings::CICD.perform do |settings|
+ settings.expand_secret_variables do |page|
+ expect(page).to have_field(with: 'VARIABLE_KEY')
+ expect(page).not_to have_field(with: 'some secret variable')
+
+ page.reveal_variables
+
+ expect(page).to have_field(with: 'some secret variable')
+ end
+ end
end
end
end
diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
new file mode 100644
index 00000000000..19d3c83758a
--- /dev/null
+++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
@@ -0,0 +1,81 @@
+require 'digest/sha1'
+
+module QA
+ feature 'cloning code using a deploy key', :core, :docker do
+ let(:runner_name) { "qa-runner-#{Time.now.to_i}" }
+ let(:key) { Runtime::RSAKey.new }
+
+ given(:project) do
+ Factory::Resource::Project.fabricate! do |resource|
+ resource.name = 'deploy-key-clone-project'
+ end
+ end
+
+ after do
+ Service::Runner.new(runner_name).remove!
+ end
+
+ scenario 'user sets up a deploy key to clone code using pipelines' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Factory::Resource::Runner.fabricate! do |resource|
+ resource.project = project
+ resource.name = runner_name
+ resource.tags = %w[qa docker]
+ resource.image = 'gitlab/gitlab-runner:ubuntu'
+ end
+
+ Factory::Resource::DeployKey.fabricate! do |resource|
+ resource.project = project
+ resource.title = 'deploy key title'
+ resource.key = key.public_key
+ end
+
+ Factory::Resource::SecretVariable.fabricate! do |resource|
+ resource.project = project
+ resource.key = 'DEPLOY_KEY'
+ resource.value = key.to_pem
+ end
+
+ project.visit!
+
+ repository_uri = Page::Project::Show.act do
+ choose_repository_clone_ssh
+ repository_location_uri
+ end
+
+ gitlab_ci = <<~YAML
+ cat-config:
+ script:
+ - mkdir -p ~/.ssh
+ - ssh-keyscan -p #{repository_uri.port} #{repository_uri.host} >> ~/.ssh/known_hosts
+ - eval $(ssh-agent -s)
+ - echo "$DEPLOY_KEY" | ssh-add -
+ - git clone #{repository_uri.git_uri}
+ - sha1sum #{project.name}/.gitlab-ci.yml
+ tags:
+ - qa
+ - docker
+ YAML
+
+ Factory::Repository::Push.fabricate! do |resource|
+ resource.project = project
+ resource.file_name = '.gitlab-ci.yml'
+ resource.commit_message = 'Add .gitlab-ci.yml'
+ resource.file_content = gitlab_ci
+ end
+
+ sha1sum = Digest::SHA1.hexdigest(gitlab_ci)
+
+ Page::Project::Show.act { wait_for_push }
+ Page::Menu::Side.act { click_ci_cd_pipelines }
+ Page::Project::Pipeline::Index.act { go_to_latest_pipeline }
+ Page::Project::Pipeline::Show.act { go_to_first_job }
+
+ Page::Project::Job::Show.perform do |job|
+ expect(job.output).to include(sha1sum)
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/project/pipelines_spec.rb b/qa/qa/specs/features/project/pipelines_spec.rb
index 1bb7730e06c..74f6474443d 100644
--- a/qa/qa/specs/features/project/pipelines_spec.rb
+++ b/qa/qa/specs/features/project/pipelines_spec.rb
@@ -69,7 +69,7 @@ module QA
tags:
- qa
- test
- script: echo "CONTENTS" > my-artifacts/artifact.txt
+ script: mkdir my-artifacts; echo "CONTENTS" > my-artifacts/artifact.txt
artifacts:
paths:
- my-artifacts/
@@ -95,7 +95,7 @@ module QA
expect(pipeline).to have_build('test-success', status: :success)
expect(pipeline).to have_build('test-failure', status: :failed)
expect(pipeline).to have_build('test-tags', status: :pending)
- expect(pipeline).to have_build('test-artifacts', status: :failed)
+ expect(pipeline).to have_build('test-artifacts', status: :success)
end
end
end
diff --git a/qa/spec/factory/base_spec.rb b/qa/spec/factory/base_spec.rb
index c5663049be8..04e04886699 100644
--- a/qa/spec/factory/base_spec.rb
+++ b/qa/spec/factory/base_spec.rb
@@ -7,6 +7,7 @@ describe QA::Factory::Base do
before do
allow(QA::Factory::Product).to receive(:new).and_return(product)
+ allow(QA::Factory::Product).to receive(:populate!).and_return(product)
end
it 'instantiates the factory and calls factory method' do
@@ -76,6 +77,7 @@ describe QA::Factory::Base do
allow(subject).to receive(:new).and_return(instance)
allow(instance).to receive(:mydep).and_return(nil)
allow(QA::Factory::Product).to receive(:new)
+ allow(QA::Factory::Product).to receive(:populate!)
end
it 'builds all dependencies first' do
@@ -89,8 +91,16 @@ describe QA::Factory::Base do
describe '.product' do
subject do
Class.new(described_class) do
+ def fabricate!
+ "any"
+ end
+
+ # Defined only to be stubbed
+ def self.find_page
+ end
+
product :token do
- page.do_something_on_page!
+ find_page.do_something_on_page!
'resulting value'
end
end
@@ -105,16 +115,17 @@ describe QA::Factory::Base do
let(:page) { spy('page') }
before do
- allow(subject).to receive(:new).and_return(factory)
+ allow(factory).to receive(:class).and_return(subject)
allow(QA::Factory::Product).to receive(:new).and_return(product)
allow(product).to receive(:page).and_return(page)
+ allow(subject).to receive(:find_page).and_return(page)
end
it 'populates product after fabrication' do
subject.fabricate!
- expect(page).to have_received(:do_something_on_page!)
expect(product.token).to eq 'resulting value'
+ expect(page).to have_received(:do_something_on_page!)
end
end
end
diff --git a/qa/spec/factory/product_spec.rb b/qa/spec/factory/product_spec.rb
index fdfb1ec90cc..f245aabbf43 100644
--- a/qa/spec/factory/product_spec.rb
+++ b/qa/spec/factory/product_spec.rb
@@ -1,9 +1,20 @@
describe QA::Factory::Product do
- let(:factory) { spy('factory') }
+ let(:factory) do
+ QA::Factory::Base.new
+ end
+
+ let(:attributes) do
+ { test: QA::Factory::Product::Attribute.new(:test, proc { 'returned' }) }
+ end
+
let(:product) { spy('product') }
+ before do
+ allow(QA::Factory::Base).to receive(:attributes).and_return(attributes)
+ end
+
describe '.populate!' do
- it 'returns a fabrication product' do
+ it 'returns a fabrication product and define factory attributes as its methods' do
expect(described_class).to receive(:new).and_return(product)
result = described_class.populate!(factory) do |instance|
@@ -11,6 +22,7 @@ describe QA::Factory::Product do
end
expect(result).to be product
+ expect(result.test).to eq('returned')
end
end
diff --git a/qa/spec/git/location_spec.rb b/qa/spec/git/location_spec.rb
new file mode 100644
index 00000000000..aef906ee836
--- /dev/null
+++ b/qa/spec/git/location_spec.rb
@@ -0,0 +1,55 @@
+describe QA::Git::Location do
+ describe '.new' do
+ context 'when URI starts with ssh://' do
+ context 'when URI has port' do
+ it 'parses correctly' do
+ uri = described_class
+ .new('ssh://git@qa.test:2222/sandbox/qa/repo.git')
+
+ expect(uri.user).to eq('git')
+ expect(uri.host).to eq('qa.test')
+ expect(uri.port).to eq(2222)
+ expect(uri.path).to eq('/sandbox/qa/repo.git')
+ end
+ end
+
+ context 'when URI does not have port' do
+ it 'parses correctly' do
+ uri = described_class
+ .new('ssh://git@qa.test/sandbox/qa/repo.git')
+
+ expect(uri.user).to eq('git')
+ expect(uri.host).to eq('qa.test')
+ expect(uri.port).to eq(22)
+ expect(uri.path).to eq('/sandbox/qa/repo.git')
+ end
+ end
+ end
+
+ context 'when URI does not start with ssh://' do
+ context 'when host does not have colons' do
+ it 'parses correctly' do
+ uri = described_class
+ .new('git@qa.test:sandbox/qa/repo.git')
+
+ expect(uri.user).to eq('git')
+ expect(uri.host).to eq('qa.test')
+ expect(uri.port).to eq(22)
+ expect(uri.path).to eq('/sandbox/qa/repo.git')
+ end
+ end
+
+ context 'when host has a colon' do
+ it 'parses correctly' do
+ uri = described_class
+ .new('[git@qa:test]:sandbox/qa/repo.git')
+
+ expect(uri.user).to eq('git')
+ expect(uri.host).to eq('qa%3Atest')
+ expect(uri.port).to eq(22)
+ expect(uri.path).to eq('/sandbox/qa/repo.git')
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/page/base_spec.rb b/qa/spec/page/base_spec.rb
index 287adf35c46..52daa9697ee 100644
--- a/qa/spec/page/base_spec.rb
+++ b/qa/spec/page/base_spec.rb
@@ -14,7 +14,7 @@ describe QA::Page::Base do
end
view 'path/to/some/_partial.html.haml' do
- element :something, 'string pattern'
+ element :another_element, 'string pattern'
end
end
end
@@ -25,11 +25,10 @@ describe QA::Page::Base do
end
it 'populates views objects with data about elements' do
- subject.views.first.elements.tap do |elements|
- expect(elements.size).to eq 2
- expect(elements).to all(be_an_instance_of QA::Page::Element)
- expect(elements.map(&:name)).to eq [:something, :something_else]
- end
+ expect(subject.elements.size).to eq 3
+ expect(subject.elements).to all(be_an_instance_of QA::Page::Element)
+ expect(subject.elements.map(&:name))
+ .to eq [:something, :something_else, :another_element]
end
end
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 103573db6be..2b6365dbc41 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -55,4 +55,25 @@ describe QA::Runtime::Env do
end
end
end
+
+ describe '.user_type' do
+ it 'returns standard if not defined' do
+ expect(described_class.user_type).to eq('standard')
+ end
+
+ it 'returns standard as defined' do
+ stub_env('GITLAB_USER_TYPE', 'standard')
+ expect(described_class.user_type).to eq('standard')
+ end
+
+ it 'returns ldap as defined' do
+ stub_env('GITLAB_USER_TYPE', 'ldap')
+ expect(described_class.user_type).to eq('ldap')
+ end
+
+ it 'returns an error if invalid user type' do
+ stub_env('GITLAB_USER_TYPE', 'foobar')
+ expect { described_class.user_type }.to raise_error(ArgumentError)
+ end
+ end
end
diff --git a/scripts/codequality b/scripts/codequality
new file mode 100755
index 00000000000..2f3ccef7d2d
--- /dev/null
+++ b/scripts/codequality
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+set -eo pipefail
+
+code_path=$(pwd)
+
+# docker run --tty will merge stderr and stdout, we don't need this on CI or
+# it will break codequality json file
+[ "$CI" != "" ] || docker_tty="--tty"
+
+# The codebase and instructions for the following image can be found at https://gitlab.com/gitlab-org/codeclimate-rubocop/wikis/home
+docker pull dev.gitlab.org:5005/gitlab/gitlab-build-images:gitlab-codeclimate-rubocop-0-52-1 > /dev/null
+docker tag dev.gitlab.org:5005/gitlab/gitlab-build-images:gitlab-codeclimate-rubocop-0-52-1 codeclimate/codeclimate-rubocop:gitlab-codeclimate-rubocop-0-52-1 > /dev/null
+
+exec docker run --rm $docker_tty --env CODECLIMATE_CODE="$code_path" \
+ --volume "$code_path":/code \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ --volume /tmp/cc:/tmp/cc \
+ "codeclimate/codeclimate:${CODECLIMATE_VERSION:-0.71.1}" "$@"
diff --git a/scripts/security-harness b/scripts/security-harness
new file mode 100755
index 00000000000..d454f44dff7
--- /dev/null
+++ b/scripts/security-harness
@@ -0,0 +1,55 @@
+#!/usr/bin/env ruby
+
+require 'digest'
+require 'fileutils'
+
+harness_path = File.expand_path('../.git/security_harness', __dir__)
+hook_path = File.expand_path("../.git/hooks/pre-push", __dir__)
+
+if File.exist?(hook_path)
+ # Deal with a pre-existing hook
+ source_sum = Digest::SHA256.hexdigest(DATA.read)
+ dest_sum = Digest::SHA256.file(hook_path).hexdigest
+
+ if source_sum != dest_sum
+ puts "#{hook_path} exists and is different from our hook!"
+ puts "Remove it and re-run this script to continue."
+
+ exit 1
+ end
+else
+ File.open(hook_path, 'w') do |file|
+ IO.copy_stream(DATA, file)
+ end
+end
+
+# Toggle the harness on or off
+if File.exist?(harness_path)
+ FileUtils.rm(harness_path)
+
+ puts "Security harness removed -- you can now push to all remotes."
+else
+ FileUtils.touch(harness_path)
+
+ puts "Security harness installed -- you will only be able to push to dev.gitlab.org!"
+end
+
+__END__
+#!/bin/sh
+
+set -e
+
+url="$2"
+harness=`dirname "$0"`/../security_harness
+
+if [ -e "$harness" ]
+then
+ if [[ "$url" != *"dev.gitlab.org"* ]]
+ then
+ echo "Pushing to remotes other than dev.gitlab.org has been disabled!"
+ echo "Run scripts/security-harness to disable this check."
+ echo
+
+ exit 1
+ fi
+fi
diff --git a/scripts/static-analysis b/scripts/static-analysis
index bdb88f3cb57..0e67eabfec1 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -7,7 +7,7 @@ require_relative '../lib/gitlab/popen/runner'
def emit_warnings(static_analysis)
static_analysis.warned_results.each do |result|
puts
- puts "**** #{result.cmd.join(' ')} had the following warnings:"
+ puts "**** #{result.cmd.join(' ')} had the following warning(s):"
puts
puts result.stderr
puts
@@ -17,7 +17,7 @@ end
def emit_errors(static_analysis)
static_analysis.failed_results.each do |result|
puts
- puts "**** #{result.cmd.join(' ')} failed with the following error:"
+ puts "**** #{result.cmd.join(' ')} failed with the following error(s):"
puts
puts result.stdout
puts result.stderr
@@ -26,15 +26,10 @@ def emit_errors(static_analysis)
end
tasks = [
- %w[bundle exec rake config_lint],
- %w[bundle exec rake flay],
- %w[bundle exec rake haml_lint],
- %w[bundle exec rake scss_lint],
+ %w[bin/rake lint:all],
%w[bundle exec license_finder],
%w[yarn run eslint],
%w[bundle exec rubocop --parallel],
- %w[bundle exec rake gettext:lint],
- %w[bundle exec rake lint:static_verification],
%w[scripts/lint-conflicts.sh],
%w[scripts/lint-rugged]
]
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 73fff6eb5ca..b7257fac608 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -109,15 +109,17 @@ describe AutocompleteController do
end
context 'limited users per page' do
- let(:per_page) { 2 }
-
before do
+ 25.times do
+ create(:user)
+ end
+
sign_in(user)
- get(:users, per_page: per_page)
+ get(:users)
end
it { expect(json_response).to be_kind_of(Array) }
- it { expect(json_response.size).to eq(per_page) }
+ it { expect(json_response.size).to eq(20) }
end
context 'unauthenticated user' do
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index 79bbc29e80d..4770e187db6 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -86,6 +86,7 @@ describe Boards::IssuesController do
context 'with unauthorized user' do
before do
+ allow(Ability).to receive(:allowed?).and_call_original
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
allow(Ability).to receive(:allowed?).with(user, :read_issue, project).and_return(false)
end
diff --git a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
new file mode 100644
index 00000000000..27f558e1b5d
--- /dev/null
+++ b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
@@ -0,0 +1,146 @@
+require 'spec_helper'
+
+describe ControllerWithCrossProjectAccessCheck do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in user
+ end
+
+ render_views
+
+ context 'When reading cross project is not allowed' do
+ before do
+ allow(Ability).to receive(:allowed).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_cross_project, :global)
+ .and_return(false)
+ end
+
+ describe '#requires_cross_project_access' do
+ controller(ApplicationController) do
+ # `described_class` is not available in this context
+ include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+
+ requires_cross_project_access :index, show: false,
+ unless: -> { unless_condition },
+ if: -> { if_condition }
+
+ def index
+ render nothing: true
+ end
+
+ def show
+ render nothing: true
+ end
+
+ def unless_condition
+ false
+ end
+
+ def if_condition
+ true
+ end
+ end
+
+ it 'renders a 404 with trying to access a cross project page' do
+ message = "This page is unavailable because you are not allowed to read "\
+ "information across multiple projects."
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(response.body).to match(/#{message}/)
+ end
+
+ it 'is skipped when the `if` condition returns false' do
+ expect(controller).to receive(:if_condition).and_return(false)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'is skipped when the `unless` condition returns true' do
+ expect(controller).to receive(:unless_condition).and_return(true)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'correctly renders an action that does not require cross project access' do
+ get :show, id: 'nothing'
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe '#skip_cross_project_access_check' do
+ controller(ApplicationController) do
+ # `described_class` is not available in this context
+ include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+
+ requires_cross_project_access
+
+ skip_cross_project_access_check index: true, show: false,
+ unless: -> { unless_condition },
+ if: -> { if_condition }
+
+ def index
+ render nothing: true
+ end
+
+ def show
+ render nothing: true
+ end
+
+ def edit
+ render nothing: true
+ end
+
+ def unless_condition
+ false
+ end
+
+ def if_condition
+ true
+ end
+ end
+
+ it 'renders a success when the check is skipped' do
+ get :index
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'is executed when the `if` condition returns false' do
+ expect(controller).to receive(:if_condition).and_return(false)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'is executed when the `unless` condition returns true' do
+ expect(controller).to receive(:unless_condition).and_return(true)
+
+ get :index
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not skip the check on an action that is not skipped' do
+ get :show, id: 'hello'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'does not skip the check on an action that was not defined to skip' do
+ get :edit, id: 'hello'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb
index d7825364ed5..c1f42bbb9d7 100644
--- a/spec/controllers/concerns/issuable_collections_spec.rb
+++ b/spec/controllers/concerns/issuable_collections_spec.rb
@@ -8,6 +8,10 @@ describe IssuableCollections do
def self.helper_method(name); end
include IssuableCollections
+
+ def finder_type
+ IssuesFinder
+ end
end
controller = klass.new
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index e8707760a5a..2be46049aab 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -84,20 +84,42 @@ describe Import::BitbucketController do
double(slug: "vim", owner: bitbucket_username, name: 'vim')
end
+ let(:project) { create(:project) }
+
before do
allow_any_instance_of(Bitbucket::Client).to receive(:repo).and_return(bitbucket_repo)
allow_any_instance_of(Bitbucket::Client).to receive(:user).and_return(bitbucket_user)
assign_session_tokens
end
+ it 'returns 200 response when the project is imported successfully' do
+ allow(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+ .and_return(double(execute: project))
+
+ post :create, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns 422 response when the project could not be imported' do
+ allow(Gitlab::BitbucketImport::ProjectCreator)
+ .to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
+ .and_return(double(execute: build(:project)))
+
+ post :create, format: :json
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
context "when the repository owner is the Bitbucket user" do
context "when the Bitbucket user and GitLab user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -107,9 +129,9 @@ describe Import::BitbucketController do
it "takes the current user's namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -120,7 +142,7 @@ describe Import::BitbucketController do
allow(controller).to receive(:current_user).and_return(user)
allow(user).to receive(:can?).and_return(false)
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -143,9 +165,9 @@ describe Import::BitbucketController do
it "takes the existing namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, bitbucket_repo.name, existing_namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -154,7 +176,7 @@ describe Import::BitbucketController do
expect(Gitlab::BitbucketImport::ProjectCreator)
.not_to receive(:new)
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -163,17 +185,17 @@ describe Import::BitbucketController do
context "when current user can create namespaces" do
it "creates the namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
- .to receive(:new).and_return(double(execute: true))
+ .to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :js }.to change(Namespace, :count).by(1)
+ expect { post :create, format: :json }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, bitbucket_repo.name, an_instance_of(Group), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -184,23 +206,23 @@ describe Import::BitbucketController do
it "doesn't create the namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
- .to receive(:new).and_return(double(execute: true))
+ .to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :js }.not_to change(Namespace, :count)
+ expect { post :create, format: :json }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, bitbucket_repo.name, user.namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
end
end
- context 'user has chosen an existing nested namespace and name for the project' do
+ context 'user has chosen an existing nested namespace and name for the project', :postgresql do
let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
@@ -212,63 +234,77 @@ describe Import::BitbucketController do
it 'takes the selected namespace and name' do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, test_name, nested_namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
+ post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :json }
end
end
- context 'user has chosen a non-existent nested namespaces and name for the project' do
+ context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :json }
end
it 'creates the namespaces' do
allow(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
+ expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :json } }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
allow(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :json }
expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
end
end
- context 'user has chosen existent and non-existent nested namespaces and name for the project' do
+ context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ before do
+ parent_namespace.add_owner(user)
+ end
+
it 'takes the selected namespace and name' do
expect(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
+ post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :json }
end
it 'creates the namespaces' do
allow(Gitlab::BitbucketImport::ProjectCreator)
.to receive(:new).with(bitbucket_repo, test_name, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
+ expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :json } }
.to change { Namespace.count }.by(2)
end
end
+
+ context 'when user can not create projects in the chosen namespace' do
+ it 'returns 422 response' do
+ other_namespace = create(:group, name: 'other_namespace')
+
+ post :create, { target_namespace: other_namespace.name, format: :json }
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+ end
end
end
diff --git a/spec/controllers/import/gitlab_controller_spec.rb b/spec/controllers/import/gitlab_controller_spec.rb
index faf1e6f63ea..e958be077c2 100644
--- a/spec/controllers/import/gitlab_controller_spec.rb
+++ b/spec/controllers/import/gitlab_controller_spec.rb
@@ -57,6 +57,7 @@ describe Import::GitlabController do
end
describe "POST create" do
+ let(:project) { create(:project) }
let(:gitlab_username) { user.username }
let(:gitlab_user) do
{ username: gitlab_username }.with_indifferent_access
@@ -75,14 +76,34 @@ describe Import::GitlabController do
assign_session_token
end
+ it 'returns 200 response when the project is imported successfully' do
+ allow(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+ .and_return(double(execute: project))
+
+ post :create, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns 422 response when the project could not be imported' do
+ allow(Gitlab::GitlabImport::ProjectCreator)
+ .to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
+ .and_return(double(execute: build(:project)))
+
+ post :create, format: :json
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
context "when the repository owner is the GitLab.com user" do
context "when the GitLab.com user and GitLab server user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -92,9 +113,9 @@ describe Import::GitlabController do
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -118,9 +139,9 @@ describe Import::GitlabController do
it "takes the existing namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, existing_namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -129,7 +150,7 @@ describe Import::GitlabController do
expect(Gitlab::GitlabImport::ProjectCreator)
.not_to receive(:new)
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -138,17 +159,17 @@ describe Import::GitlabController do
context "when current user can create namespaces" do
it "creates the namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
- .to receive(:new).and_return(double(execute: true))
+ .to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :js }.to change(Namespace, :count).by(1)
+ expect { post :create, format: :json }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, an_instance_of(Group), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -159,22 +180,22 @@ describe Import::GitlabController do
it "doesn't create the namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
- .to receive(:new).and_return(double(execute: true))
+ .to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :js }.not_to change(Namespace, :count)
+ expect { post :create, format: :json }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, user.namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
end
- context 'user has chosen an existing nested namespace for the project' do
+ context 'user has chosen an existing nested namespace for the project', :postgresql do
let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
@@ -185,64 +206,78 @@ describe Import::GitlabController do
it 'takes the selected namespace and name' do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, nested_namespace, user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: nested_namespace.full_path, format: :js }
+ post :create, { target_namespace: nested_namespace.full_path, format: :json }
end
end
- context 'user has chosen a non-existent nested namespaces for the project' do
+ context 'user has chosen a non-existent nested namespaces for the project', :postgresql do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/bar', format: :js }
+ post :create, { target_namespace: 'foo/bar', format: :json }
end
it 'creates the namespaces' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- expect { post :create, { target_namespace: 'foo/bar', format: :js } }
+ expect { post :create, { target_namespace: 'foo/bar', format: :json } }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/bar', format: :js }
+ post :create, { target_namespace: 'foo/bar', format: :json }
expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
end
end
- context 'user has chosen existent and non-existent nested namespaces and name for the project' do
+ context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ before do
+ parent_namespace.add_owner(user)
+ end
+
it 'takes the selected namespace and name' do
expect(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/foobar/bar', format: :js }
+ post :create, { target_namespace: 'foo/foobar/bar', format: :json }
end
it 'creates the namespaces' do
allow(Gitlab::GitlabImport::ProjectCreator)
.to receive(:new).with(gitlab_repo, kind_of(Namespace), user, access_params)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- expect { post :create, { target_namespace: 'foo/foobar/bar', format: :js } }
+ expect { post :create, { target_namespace: 'foo/foobar/bar', format: :json } }
.to change { Namespace.count }.by(2)
end
end
+
+ context 'when user can not create projects in the chosen namespace' do
+ it 'returns 422 response' do
+ other_namespace = create(:group, name: 'other_namespace')
+
+ post :create, { target_namespace: other_namespace.name, format: :json }
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+ end
end
end
end
diff --git a/spec/controllers/profiles_controller_spec.rb b/spec/controllers/profiles_controller_spec.rb
index d380978b86e..03cbbb21e62 100644
--- a/spec/controllers/profiles_controller_spec.rb
+++ b/spec/controllers/profiles_controller_spec.rb
@@ -69,9 +69,8 @@ describe ProfilesController, :request_store do
describe 'PUT update_username' do
let(:namespace) { user.namespace }
- let(:project) { create(:project_empty_repo, namespace: namespace) }
let(:gitlab_shell) { Gitlab::Shell.new }
- let(:new_username) { 'renamedtosomethingelse' }
+ let(:new_username) { generate(:username) }
it 'allows username change' do
sign_in(user)
@@ -85,16 +84,39 @@ describe ProfilesController, :request_store do
expect(user.username).to eq(new_username)
end
- it 'moves dependent projects to new namespace' do
- sign_in(user)
+ context 'with legacy storage' do
+ it 'moves dependent projects to new namespace' do
+ project = create(:project_empty_repo, :legacy_storage, namespace: namespace)
- put :update_username,
- user: { username: new_username }
+ sign_in(user)
- user.reload
+ put :update_username,
+ user: { username: new_username }
- expect(response.status).to eq(302)
- expect(gitlab_shell.exists?(project.repository_storage_path, "#{new_username}/#{project.path}.git")).to be_truthy
+ user.reload
+
+ expect(response.status).to eq(302)
+ expect(gitlab_shell.exists?(project.repository_storage_path, "#{new_username}/#{project.path}.git")).to be_truthy
+ end
+ end
+
+ context 'with hashed storage' do
+ it 'keeps repository location unchanged on disk' do
+ project = create(:project_empty_repo, namespace: namespace)
+
+ before_disk_path = project.disk_path
+
+ sign_in(user)
+
+ put :update_username,
+ user: { username: new_username }
+
+ user.reload
+
+ expect(response.status).to eq(302)
+ expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_truthy
+ expect(before_disk_path).to eq(project.disk_path)
+ end
end
end
end
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index 775f9db1c6e..e14ba29fa70 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -161,7 +161,7 @@ describe Projects::Clusters::GcpController do
it 'renders the cluster form with an error' do
go
- expect(response).to set_flash[:alert]
+ expect(response).to set_flash.now[:alert]
expect(response).to render_template('new')
end
end
diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
index 92db7284e0e..24310b847e8 100644
--- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb
@@ -17,7 +17,7 @@ describe Projects::MergeRequests::CreationsController do
before do
fork_project.add_master(user)
-
+ Projects::ForkService.new(project, user).execute(fork_project)
sign_in(user)
end
@@ -125,4 +125,66 @@ describe Projects::MergeRequests::CreationsController do
end
end
end
+
+ describe 'GET #branch_to' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ end
+
+ it 'fetches the commit if a user has access' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true }
+
+ get :branch_to,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id,
+ ref: 'master'
+
+ expect(assigns(:commit)).not_to be_nil
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'does not load the commit when the user cannot read the project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false }
+
+ get :branch_to,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id,
+ ref: 'master'
+
+ expect(assigns(:commit)).to be_nil
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
+ describe 'GET #update_branches' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ end
+
+ it 'lists the branches of another fork if the user has access' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true }
+
+ get :update_branches,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id
+
+ expect(assigns(:target_branches)).not_to be_empty
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'does not list branches when the user cannot read the project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false }
+
+ get :update_branches,
+ namespace_id: fork_project.namespace,
+ project_id: fork_project,
+ target_project_id: project.id
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(assigns(:target_branches)).to eq([])
+ end
+ end
end
diff --git a/spec/controllers/projects/pages_domains_controller_spec.rb b/spec/controllers/projects/pages_domains_controller_spec.rb
index e9e7d357d9c..2192fd5cae2 100644
--- a/spec/controllers/projects/pages_domains_controller_spec.rb
+++ b/spec/controllers/projects/pages_domains_controller_spec.rb
@@ -46,7 +46,46 @@ describe Projects::PagesDomainsController do
post(:create, request_params.merge(pages_domain: pages_domain_params))
end.to change { PagesDomain.count }.by(1)
- expect(response).to redirect_to(project_pages_path(project))
+ created_domain = PagesDomain.reorder(:id).last
+
+ expect(created_domain).to be_present
+ expect(response).to redirect_to(project_pages_domain_path(project, created_domain))
+ end
+ end
+
+ describe 'POST verify' do
+ let(:params) { request_params.merge(id: pages_domain.domain) }
+
+ def stub_service
+ service = double(:service)
+
+ expect(VerifyPagesDomainService).to receive(:new) { service }
+
+ service
+ end
+
+ it 'handles verification success' do
+ expect(stub_service).to receive(:execute).and_return(status: :success)
+
+ post :verify, params
+
+ expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(flash[:notice]).to eq('Successfully verified domain ownership')
+ end
+
+ it 'handles verification failure' do
+ expect(stub_service).to receive(:execute).and_return(status: :failed)
+
+ post :verify, params
+
+ expect(response).to redirect_to project_pages_domain_path(project, pages_domain)
+ expect(flash[:alert]).to eq('Failed to verify domain ownership')
+ end
+
+ it 'returns a 404 response for an unknown domain' do
+ post :verify, request_params.merge(id: 'unknown-domain')
+
+ expect(response).to have_gitlab_http_status(404)
end
end
diff --git a/spec/controllers/projects/prometheus_controller_spec.rb b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
index bbfe78d305a..f17f819feee 100644
--- a/spec/controllers/projects/prometheus_controller_spec.rb
+++ b/spec/controllers/projects/prometheus/metrics_controller_spec.rb
@@ -1,20 +1,20 @@
-require('spec_helper')
+require 'spec_helper'
-describe Projects::PrometheusController do
+describe Projects::Prometheus::MetricsController do
let(:user) { create(:user) }
- let!(:project) { create(:project) }
+ let(:project) { create(:project) }
let(:prometheus_service) { double('prometheus_service') }
before do
allow(controller).to receive(:project).and_return(project)
- allow(project).to receive(:prometheus_service).and_return(prometheus_service)
+ allow(project).to receive(:find_or_initialize_service).with('prometheus').and_return(prometheus_service)
project.add_master(user)
sign_in(user)
end
- describe 'GET #active_metrics' do
+ describe 'GET #active_common' do
context 'when prometheus metrics are enabled' do
context 'when data is not present' do
before do
@@ -22,7 +22,7 @@ describe Projects::PrometheusController do
end
it 'returns no content response' do
- get :active_metrics, project_params(format: :json)
+ get :active_common, project_params(format: :json)
expect(response).to have_gitlab_http_status(204)
end
@@ -36,7 +36,7 @@ describe Projects::PrometheusController do
end
it 'returns no content response' do
- get :active_metrics, project_params(format: :json)
+ get :active_common, project_params(format: :json)
expect(response).to have_gitlab_http_status(200)
expect(json_response).to eq(sample_response.deep_stringify_keys)
@@ -45,7 +45,7 @@ describe Projects::PrometheusController do
context 'when requesting non json response' do
it 'returns not found response' do
- get :active_metrics, project_params
+ get :active_common, project_params
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/controllers/projects/uploads_controller_spec.rb b/spec/controllers/projects/uploads_controller_spec.rb
index d572085661d..eca9baed9c9 100644
--- a/spec/controllers/projects/uploads_controller_spec.rb
+++ b/spec/controllers/projects/uploads_controller_spec.rb
@@ -7,4 +7,12 @@ describe Projects::UploadsController do
end
it_behaves_like 'handle uploads'
+
+ context 'when the URL the old style, without /-/system' do
+ it 'responds with a redirect to the login page' do
+ get :show, namespace_id: 'project', project_id: 'avatar', filename: 'foo.png', secret: 'bar'
+
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 5202ffdd8bb..994da3cd159 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -288,62 +288,82 @@ describe ProjectsController do
render_views
let(:admin) { create(:admin) }
- let(:project) { create(:project, :repository) }
before do
sign_in(admin)
end
- context 'when only renaming a project path' do
- it "sets the repository to the right path after a rename" do
- expect { update_project path: 'renamed_path' }
- .to change { project.reload.path }
+ shared_examples_for 'updating a project' do
+ context 'when only renaming a project path' do
+ it "sets the repository to the right path after a rename" do
+ original_repository_path = project.repository.path
- expect(project.path).to include 'renamed_path'
- expect(assigns(:repository).path).to include project.path
- expect(response).to have_gitlab_http_status(302)
- end
- end
+ expect { update_project path: 'renamed_path' }
+ .to change { project.reload.path }
+ expect(project.path).to include 'renamed_path'
- context 'when project has container repositories with tags' do
- before do
- stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: /image/, tags: %w[rc1])
- create(:container_repository, project: project, name: :image)
+ if project.hashed_storage?(:repository)
+ expect(assigns(:repository).path).to eq(original_repository_path)
+ else
+ expect(assigns(:repository).path).to include(project.path)
+ end
+
+ expect(response).to have_gitlab_http_status(302)
+ end
end
- it 'does not allow to rename the project' do
- expect { update_project path: 'renamed_path' }
- .not_to change { project.reload.path }
+ context 'when project has container repositories with tags' do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: /image/, tags: %w[rc1])
+ create(:container_repository, project: project, name: :image)
+ end
- expect(controller).to set_flash[:alert].to(/container registry tags/)
- expect(response).to have_gitlab_http_status(200)
+ it 'does not allow to rename the project' do
+ expect { update_project path: 'renamed_path' }
+ .not_to change { project.reload.path }
+
+ expect(controller).to set_flash[:alert].to(/container registry tags/)
+ expect(response).to have_gitlab_http_status(200)
+ end
end
- end
- it 'updates Fast Forward Merge attributes' do
- controller.instance_variable_set(:@project, project)
+ it 'updates Fast Forward Merge attributes' do
+ controller.instance_variable_set(:@project, project)
- params = {
- merge_method: :ff
- }
+ params = {
+ merge_method: :ff
+ }
- put :update,
- namespace_id: project.namespace,
- id: project.id,
- project: params
+ put :update,
+ namespace_id: project.namespace,
+ id: project.id,
+ project: params
- expect(response).to have_gitlab_http_status(302)
- params.each do |param, value|
- expect(project.public_send(param)).to eq(value)
+ expect(response).to have_gitlab_http_status(302)
+ params.each do |param, value|
+ expect(project.public_send(param)).to eq(value)
+ end
+ end
+
+ def update_project(**parameters)
+ put :update,
+ namespace_id: project.namespace.path,
+ id: project.path,
+ project: parameters
end
end
- def update_project(**parameters)
- put :update,
- namespace_id: project.namespace.path,
- id: project.path,
- project: parameters
+ context 'hashed storage' do
+ let(:project) { create(:project, :repository) }
+
+ it_behaves_like 'updating a project'
+ end
+
+ context 'legacy storage' do
+ let(:project) { create(:project, :repository, :legacy_storage) }
+
+ it_behaves_like 'updating a project'
end
end
diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb
index 37f961d0c94..30c06ddf744 100644
--- a/spec/controllers/search_controller_spec.rb
+++ b/spec/controllers/search_controller_spec.rb
@@ -16,6 +16,32 @@ describe SearchController do
expect(assigns[:search_objects].first).to eq note
end
+ context 'when the user cannot read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?)
+ .with(user, :read_cross_project, :global) { false }
+ end
+
+ it 'still allows accessing the search page' do
+ get :show
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'still blocks searches without a project_id' do
+ get :show, search: 'hello'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'allows searches with a project_id' do
+ get :show, search: 'hello', project_id: create(:project, :public).id
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+ end
+
context 'on restricted projects' do
context 'when signed out' do
before do
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 2898c4b119e..b0acf4a49ac 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -74,6 +74,31 @@ describe UsersController do
end
end
end
+
+ context 'json with events' do
+ let(:project) { create(:project) }
+ before do
+ project.add_developer(user)
+ Gitlab::DataBuilder::Push.build_sample(project, user)
+
+ sign_in(user)
+ end
+
+ it 'loads events' do
+ get :show, username: user, format: :json
+
+ expect(assigns(:events)).not_to be_empty
+ end
+
+ it 'hides events if the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ get :show, username: user, format: :json
+
+ expect(assigns(:events)).to be_empty
+ end
+ end
end
describe 'GET #calendar' do
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 6ba599cdf83..f6ba3a581ca 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -180,8 +180,8 @@ FactoryBot.define do
trait :artifacts do
after(:create) do |build|
- create(:ci_job_artifact, :archive, job: build)
- create(:ci_job_artifact, :metadata, job: build)
+ create(:ci_job_artifact, :archive, job: build, expire_at: build.artifacts_expire_at)
+ create(:ci_job_artifact, :metadata, job: build, expire_at: build.artifacts_expire_at)
build.reload
end
end
diff --git a/spec/factories/keys.rb b/spec/factories/keys.rb
index 23a98a899f1..3f0c60f32b7 100644
--- a/spec/factories/keys.rb
+++ b/spec/factories/keys.rb
@@ -22,38 +22,104 @@ FactoryBot.define do
factory :rsa_key_2048 do
key do
<<~KEY.delete("\n")
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFf6RYK3qu/RKF/3ndJmL5xgMLp3O9
- 6x8lTay+QGZ0+9FnnAXMdUqBq/ZU6d/gyMB4IaW3nHzM1w049++yAB6UPCzMB8Uo27K5
- /jyZCtj7Vm9PFNjF/8am1kp46c/SeYicQgQaSBdzIW3UDEa1Ef68qroOlvpi9PYZ/tA7
- M0YP0K5PXX+E36zaIRnJVMPT3f2k+GnrxtjafZrwFdpOP/Fol5BQLBgcsyiU+LM1SuaC
- rzd8c9vyaTA1CxrkxaZh+buAi0PmdDtaDrHd42gqZkXCKavyvgM5o2CkQ5LJHCgzpXy0
- 5qNFzmThBSkb+XtoxbyagBiGbVZtSVow6Xa7qewz= dummy@gitlab.com
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC98dbu7gxcbmAvwMqz/6AALhSr1jiX
+ G0UC8FQMvoDt+ciB+uSJhg7KlxinKjYJnPGfhX+q2K+mmCGAmI/D6q7rFxE+bn09O+75
+ qgkTHi+suDVE6KG7L3n0alGd/qSevfomR77Snh6fQPdG6sEAZz3kehcpfVnq5/IuLFq9
+ FBrgmu52Jd4XZLQZKkDq6zYOJ69FUkGf93LZIV/OOaS+f+qkOGPCUkdKl7oEcgpVNY9S
+ RjBCduXnvi2CyQnnJVkBguGL5VlXwFXH+17Whs7oFWmdiG+4jzBRLIMz4EuIW09b8Su5
+ PW6+bBuXOifHA8KG5TMmjs5LYdCMPFnhTyDyO3a1 dummy@gitlab.com
KEY
end
factory :rsa_deploy_key_2048, class: 'DeployKey'
end
+ factory :rsa_key_4096 do
+ key do
+ <<~KEY.delete("\n")
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDGSD77lLtjmzewiBs6nu2R5nu6oNkrA
+ kH/0co1fHHosKfRr+sWkSTKXOVcL7bhRu+tniGBmB5pn+i1qX7BXtrcnv//bCXWIp+me0
+ 27L4RJa5/Ep077iiTJlzTpcV664xNUXC8mzBr601HR/Z2TzX5DWJvnyqqFkN7qHTYo/+I
+ oKECnKqNzI5SQrAxgi6sbWA5DFQ/nwcqsUSBo5gCCJ/0QPrR19yVV5lJA19EY2LawOb1S
+ JNOFo4mQupSlBZwvERZJ7IqhBTPtQIfrqqz5VJbI13jK3ViZTugIZqydWAhosUyejP3Sd
+ Cj1KMexrvV95tjUtmhVFlph4tKThQO0p9pXKZNCzYsbQTye6O6Hk2rojOJLyFWqNBVKtI
+ 8Ymfu7OQWppRnuUFuhuuS515H1s888bZFMPsC74mPyo0Y7Q9wAoTnQ9Hw6b0J6OfY3PIR
+ VphaCmxh6b7dgSPFdD7TA6j0xk6PCTOIEzBKuc85B3GQc8Nt4sTv6fW8lGeuYWqepW74i
+ geC4qB6U3/3+p3nPdq/bTM1txrhnQsl1r4dv6TLZ51EtHp6sXayp0qd0pRaiavebXFC0i
+ aETLraQpye4FWbBL/8xTjQ/0VPrYVuUCDvDSMIIS3/9g7Kp7ERUDC9jUqOVonm4pTXL9i
+ ItiUBlK7Mob9C4fQIRFnVR00DCmkmVgw== dummy@gitlab.com
+ KEY
+ end
+ end
+
+ factory :rsa_key_5120 do
+ key do
+ <<~KEY.delete("\n")
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACgQDxnZP0TucLH3zcrvt75DPNq+xKqOmJk
+ CEzTytKq4S5MDH0nlx+xOZ9WykhwDHXU0iZBJF7yRdLkZweYDJVKnBzr4t7QP5Sw2/ZdL
+ elvUMWGJjuz28x8Z+8NZ+IxL/exDz7itrhCsLupQhGO1obiIwf8xVzzPoxrQ9dxaN4x96
+ 5N+QdQcld8O6xfpSE0p5Y3sRn3kp57aHWoNa/bUGZy0OHLr/ig0uc6EKyWsTmEESOgDyV
+ 94wOyHR0KNGEENyxQt4BwAbEBn3Y41HKqD358KKh+XjbECebrrBFigdDL/eYFIUlstJ07
+ SK/HtYjZbiUZCPs8bJA+SBaLK0pGGqguM2LXRoMeMUZFwKKKS2LpRqjKGj3Qt7qMnp1Sk
+ VhiMnxNqL4nJnDOOVo07xDIPKqIBYO67/cp4Icv3IjKxy6K3EIpLr+iRCxcllpDogxolz
+ FC+pEDVpmEvcrGEv1ON6HcCdk/6Q8Iekr8rYDHpKCU5FF2uBHkqq7yNJ1/+NFC4dgyOo0
+ xCVL4D3DvDKNxFYkrzW4ICt0f5XcMnU10yS/OFXz8JwA3jvuLvMRe5JdFiIjb/l86+TgY
+ yvK8Y8N/UWgSgyjXUCv8nxdvpsxdz5h7HBF8E2DIxCVMC23655e5rp5eJW9EU9X5YFZc3
+ u6uWJ1f1aO+1ViTtqkPrqxovNDD+gVel8Ny6MJ4MvmDKY+eM8beNMSSf1n1Oyh/SvCffh
+ ZpUqrXdTr9qwZEOaC75T74AJ7KBl9VvO3vPLZuJrt38R2OZG/4SlNEUA6bb5TWQLtdor/
+ qpPN5jAskkAUzOh5L/M+dmq2jNn03U9xwORCYPZj+fFM9bL99/0knsV0ypZDZyWH dummy@gitlab.com
+ KEY
+ end
+ end
+
+ factory :rsa_key_8192 do
+ key do
+ <<~KEY.delete("\n")
+ ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAEAQC5jMyGtgMOVX4t2GuXkbirJA0Edr+ql
+ OH9grnRBPHPo0Npt6XE6ZN3J3hDULTQo03wmekGw42dxdNSgk+F0GjsUBrMLbqrk485MM
+ e0cUbP4lRXNu4ao87wPVM5fAsD4E3FQiZcI6Df011ZGIL7hGTHt6eafTfr9cJheRyYSu6
+ g06rlnFWbbtSh9oQ7Y6sfDLBcsC9ECcXwe3mwViuQXPIVomZ02EdnBbAhbGHDtA+ZbSvT
+ fraxOMjkxkVvvdjLxXEykpwVuZf8eZ+R/Js8jQ5RKvTZMbfxJNsGEqHD32s43ml4VF549
+ Qz2GJDXF7Cld/n3CT6wvw0mMPM0LnykL2v0CMr44bjIA3KsNEs5MhkcBO8sv5hGfcPhrp
+ m9WwI6gd9vdZVcxarVI+iQS947owvdn4VbEZXynCDqEEv3Zh+FA5p23mf2p7DkG/swiK/
+ IPrjr1wmsiWmwIUsENzJNyJtibKuRsBawC4ZdL797tFilSoTzSpriegSL13joPXz3eOHC
+ Vu4ATHMo3QyLfIFbxrf9PQ79nyOpHoX2YeFXvei3xFkGMundkOqeI+pnJKDyqbiLV7UVl
+ clua11QWNQZf1ZUd0n1wZ1g89de+wl3oJSRbSA5ZpveZEPstcMC/JhogY4JBYsvCT1yHO
+ oNWHo90NZQsUCjNnR+/FVaACtpt2zcPTjjbXvxwCDlT3gXTmTBp/kEZq6u8p+BOlqFgxc
+ P/sdAR8jWTin3Iw/YAcbqNgRHdjMUzJBrPQ5NcK6xFcmkOEQahdJDZs98xozCHkD4Urx6
+ +auTr/uqRYobKoNUNiYqN1n7/dfZjQJJVkHtKd06JTFx+7/SqyfrTKS+/EIf2Hypdy9r9
+ IFR+SWAOi11N/wflS/ZbH95Qt3STifXRecmHzyYGkMOZ+mg3Hi2YU0yn7k+P1jy627xud
+ pT9Ak3HWT5ji8tMyn9udL7m80dYpUiEAxoYZdbSSNCDaKP4ViABnGIeZreIujabI8IdtE
+ IjFQTaF2d5HTYjp28/qf576CFP5L7AGydypipYqZUmsYnay5YVjdm89He3TMD71SwspJl
+ POC4RnM0HS87OE+U0+mVaIe8YYbcjTekpVU9mkqsE/GQ34Egw79VMNNgWq5avOzpT8msC
+ lTJxgfJ1agGgigTvGxUM0FB07+sIdJxxNymAGpLKZ1op8xaJI3o8D86jWgI22za1zxUB5
+ il9U7+KOzaWo9mp3bmhvZWGDwzTXEZhUJYMRby7o6UxSHlA6fKE63JSDD2yhXk4CjsQRN
+ C7Ph9cYSB+Wa3i9Am4rRlJgrF79okmEOMpj1idliHkpIsy/k2CN9Lf2EIHOD4NMuLrSUH
+ 4qJsPUq19ZbGIMdImD3vMS5b dummy@gitlab.com
+ KEY
+ end
+ end
+
factory :dsa_key_2048 do
key do
<<~KEY.delete("\n")
- ssh-dss AAAAB3NzaC1kc3MAAAEBAO/3/NPLA/zSFkMOCaTtGo+uos1flfQ5f038Uk+G
- Y9AeLGzX+Srhw59GdVXmOQLYBrOt5HdGwqYcmLnE2VurUGmhtfeO5H+3p5pGJbkS0Gxp
- YH1HRO9lWsncF3Hh1w4lYsDjkclDiSTdfTuN8F4Kb3DXNnVSCieeonp+B25F/CXagyTQ
- /pvNmHFeYgGCVdnBtFdi+xfxaZ8NKdPrGggzokbKHElDZQ4Xo5EpdcyLajgM7nB2r2Rz
- OrmeaevKi5lV68ehRa9Yyrb7vxvwiwBwOgqR/mnN7Gnaq1jUdmJY+ct04Qwx37f5jvhv
- 5gA4U40SGMoiHM8RFIN7Ksz0jsyX73MAAAAVALRWOfjfzHpK7KLz4iqDvvTUAevJAAAB
- AEa9NZ+6y9iQ5erGsdfLTXFrhSefTG0NhghoO/5IFkSGfd8V7kzTvCHaFrcfpEA5kP8t
- poeOG0TASB6tgGOxm1Bq4Wncry5RORBPJlAVpDGRcvZ931ddH7IgltEInS6za2uH6F/1
- M1QfKePSLr6xJ1ZLYfP0Og5KTp1x6yMQvfwV0a+XdA+EPgaJWLWp/pWwKWa0oLUgjsIH
- MYzuOGh5c708uZrmkzqvgtW2NgXhcIroRgynT3IfI2lP2rqqb3uuuE/qH5UCUFO+Dc3H
- nAFNeQDT/M25AERdPYBAY5a+iPjIgO+jT7BfmfByT+AZTqZySrCyc7nNZL3YgGLK0l6A
- 1GgAAAEBAN9FpFOdIXE+YEZhKl1vPmbcn+b1y5zOl6N4x1B7Q8pD/pLMziWROIS8uLzb
- aZ0sMIWezHIkxuo1iROMeT+jtCubn7ragaN6AX7nMpxYUH9+mYZZs/fyElt6wCviVhTI
- zM+u7VdQsnZttOOlQfogHdL+SpeAft0DsfJjlcgQnsLlHQKv6aPqCPYUST2nE7RyW/Ex
- PrMxLtOWt0/j8RYHbwwqvyeZqBz3ESBgrS9c5tBdBfauwYUV/E7gPLOU3OZFw9ue7o+z
- wzoTZqW6Xouy5wtWvSLQSLT5XwOslmQz8QMBxD0AQyDfEFGsBCWzmbTgKv9uqrBjubsS
- Taja+Cf9kMo== dummy@gitlab.com
+ ssh-dss AAAAB3NzaC1kc3MAAAEBALEB3sM2kPy6LKLiyL+UlDx2vzuKrzSD2nsW2Kb7
+ 0ivIqDNJu5CbqIQSkjdMzJiocs33ESFqXid6ezOtVdDwXHJQRxKGalW1kBbFAPjtMxlD
+ bf559+7qN2zfCfcQsgTmNAZ7O+wltqJmyLv5i4QqNwPDvyeBvJ4C+770DzlcQtpkflKJ
+ X+O7i8Ylq34h6UTCTnjry+dFVm1xz97LPf7XuzXGZcAG/eGUNQgxQ2bferKnrpYOXx6c
+ ocSRj9W54nrRFMWuDeOspWp4MoYK0FRMfDQYPksUayGUnm1KQTGuDbB0ahRNCOm8b3tf
+ P9Z+vjANAkqenzDuXCpz2PU/Oj6/N/UAAAAhAPOLyut12Mjcp3eUXLe1xSoI5IRXSLso
+ W9no93dcFNprAAABAQCLhpqKY+PNcwbhhPruL+f+uROghHzDwRNX+e231F4wHHeDDomf
+ WyLVFj31XrHdDXZnS9tTTj5D2XWLovSSxYb3H7earTctmktL0lQ3HapujzvOkn+VM0pG
+ s6B3j54+AM3mg50KZdYWxxv+v/lb6oEcsCjfKNyRIx/5pqX6XI3dxl9MMIxrfVWpkNX+
+ FI68v1LVV61DC9PkNyEHU0v9YBOfrTiS21TIlVIZcSFhuDjg52MekfZAnoKaP7YFJNF3
+ fdCrXaU3hYQrwB9XdskBUppwxKGhf7O6SWEZhAEfPA9kgxaWHoJvsDz8aca576UNe7BP
+ mjzo/SLUX+P4uvcaffd+AAABAEqzpmwjzTxB+DV8C+0LnmKf3L/UlQWyGdmhd65rnbkH
+ GgRMAAkoh4GBOEHL5bznNRmO7X/H6g2fR7SEabxfbvb903KI4nbfFF+3QtnwyIbTBAcH
+ 0893D3bi5rsaJcz+c6lBob2En2nThRciefXUk2oPzCQuDyFIyHLJikqRQVcalHCdQ00c
+ /H/JkiJedHNqaeU4TeMk8SM53Brjplj/iiJq+ujc5MlEgACdCwWp0BviFACEoYyFaa3R
+ kc7Xdm9vFpclm9fzgUfPloASA0SkO945in3mIqMfODTb4yRvbjk8If9483fEPgQkczpd
+ ptBz1VAKg8AmRcz1GmBIxs+Stn0= dummy@gitlab.com
KEY
end
end
diff --git a/spec/factories/pages_domains.rb b/spec/factories/pages_domains.rb
index 61b04708da2..35b44e1c52e 100644
--- a/spec/factories/pages_domains.rb
+++ b/spec/factories/pages_domains.rb
@@ -1,6 +1,25 @@
FactoryBot.define do
factory :pages_domain, class: 'PagesDomain' do
- domain 'my.domain.com'
+ sequence(:domain) { |n| "my#{n}.domain.com" }
+ verified_at { Time.now }
+ enabled_until { 1.week.from_now }
+
+ trait :disabled do
+ verified_at nil
+ enabled_until nil
+ end
+
+ trait :unverified do
+ verified_at nil
+ end
+
+ trait :reverify do
+ enabled_until { 1.hour.from_now }
+ end
+
+ trait :expired do
+ enabled_until { 1.hour.ago }
+ end
trait :with_certificate do
certificate '-----BEGIN CERTIFICATE-----
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index f92b307fee4..1761b6e2a3b 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -81,8 +81,10 @@ FactoryBot.define do
archived true
end
- trait :hashed do
- storage_version Project::LATEST_STORAGE_VERSION
+ storage_version Project::LATEST_STORAGE_VERSION
+
+ trait :legacy_storage do
+ storage_version nil
end
trait :access_requestable do
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index a01c129defd..7eeed7da998 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -19,7 +19,7 @@ describe "Admin Runners" do
end
it 'has all necessary texts' do
- expect(page).to have_text "How to setup"
+ expect(page).to have_text "Setup a shared Runner manually"
expect(page).to have_text "Runners with last contact more than a minute ago: 1"
end
@@ -54,7 +54,7 @@ describe "Admin Runners" do
end
it 'has all necessary texts including no runner message' do
- expect(page).to have_text "How to setup"
+ expect(page).to have_text "Setup a shared Runner manually"
expect(page).to have_text "Runners with last contact more than a minute ago: 0"
expect(page).to have_text 'No runners found'
end
diff --git a/spec/features/admin/services/admin_activates_prometheus_spec.rb b/spec/features/admin/services/admin_activates_prometheus_spec.rb
new file mode 100644
index 00000000000..904fe5b406b
--- /dev/null
+++ b/spec/features/admin/services/admin_activates_prometheus_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe 'Admin activates Prometheus' do
+ let(:admin) { create(:user, :admin) }
+
+ before do
+ sign_in(admin)
+
+ visit(admin_application_settings_services_path)
+
+ click_link('Prometheus')
+ end
+
+ it 'activates service' do
+ check('Active')
+ fill_in('API URL', with: 'http://prometheus.example.com')
+ click_button('Save')
+
+ expect(page).to have_content('Application settings saved successfully')
+ end
+end
diff --git a/spec/features/auto_deploy_spec.rb b/spec/features/auto_deploy_spec.rb
deleted file mode 100644
index 9aef68b7156..00000000000
--- a/spec/features/auto_deploy_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'spec_helper'
-
-describe 'Auto deploy' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
-
- shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
- context 'when no deployment service is active' do
- before do
- trun_off
- end
-
- it 'does not show a button to set up auto deploy' do
- visit project_path(project)
- expect(page).to have_no_content('Set up auto deploy')
- end
- end
-
- context 'when a deployment service is active' do
- before do
- trun_on
- visit project_path(project)
- end
-
- it 'shows a button to set up auto deploy' do
- expect(page).to have_link('Set up auto deploy')
- end
-
- it 'includes OpenShift as an available template', :js do
- click_link 'Set up auto deploy'
- click_button 'Apply a GitLab CI Yaml template'
-
- within '.gitlab-ci-yml-selector' do
- expect(page).to have_content('OpenShift')
- end
- end
-
- it 'creates a merge request using "auto-deploy" branch', :js do
- click_link 'Set up auto deploy'
- click_button 'Apply a GitLab CI Yaml template'
- within '.gitlab-ci-yml-selector' do
- click_on 'OpenShift'
- end
- wait_for_requests
- click_button 'Commit changes'
-
- expect(page).to have_content('New Merge Request From auto-deploy into master')
- end
- end
- end
-
- context 'when user configured kubernetes from Integration > Kubernetes' do
- before do
- create :kubernetes_service, project: project
- project.add_master(user)
- sign_in user
- end
-
- let(:trun_on) { project.deployment_platform.update!(active: true) }
- let(:trun_off) { project.deployment_platform.update!(active: false) }
-
- it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
- end
-
- context 'when user configured kubernetes from CI/CD > Clusters' do
- before do
- create(:cluster, :provided_by_gcp, projects: [project])
- project.add_master(user)
- sign_in user
- end
-
- let(:trun_on) { project.deployment_platform.cluster.update!(enabled: true) }
- let(:trun_off) { project.deployment_platform.cluster.update!(enabled: false) }
-
- it_behaves_like 'same behavior between KubernetesService and Platform::Kubernetes'
- end
-end
diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb
index 9bc23baf6cf..b1dceec9da8 100644
--- a/spec/features/ci_lint_spec.rb
+++ b/spec/features/ci_lint_spec.rb
@@ -3,16 +3,19 @@ require 'spec_helper'
describe 'CI Lint', :js do
before do
sign_in(create(:user))
+
+ visit ci_lint_path
+ find('#ci-editor')
+ execute_script("ace.edit('ci-editor').setValue(#{yaml_content.to_json});")
+
+ # Ace editor updates a hidden textarea and it happens asynchronously
+ wait_for('YAML content') do
+ find('.ace_content').text.present?
+ end
end
describe 'YAML parsing' do
before do
- visit ci_lint_path
- # Ace editor updates a hidden textarea and it happens asynchronously
- # `sleep 0.1` is actually needed here because of this
- find('#ci-editor')
- execute_script("ace.edit('ci-editor').setValue(" + yaml_content.to_json + ");")
- sleep 0.1
click_on 'Validate'
end
@@ -32,11 +35,10 @@ describe 'CI Lint', :js do
end
context 'YAML is incorrect' do
- let(:yaml_content) { '' }
+ let(:yaml_content) { 'value: cannot have :' }
it 'displays information about an error' do
expect(page).to have_content('Status: syntax is incorrect')
- expect(page).to have_content('Error: Please provide content of .gitlab-ci.yml')
end
end
@@ -48,4 +50,20 @@ describe 'CI Lint', :js do
end
end
end
+
+ describe 'YAML clearing' do
+ before do
+ click_on 'Clear'
+ end
+
+ context 'YAML is present' do
+ let(:yaml_content) do
+ File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ end
+
+ it 'YAML content is cleared' do
+ expect(page).to have_field('content', with: '', visible: false, type: 'textarea')
+ end
+ end
+ end
end
diff --git a/spec/features/groups/empty_states_spec.rb b/spec/features/groups/empty_states_spec.rb
index 243e8536168..04217fec06c 100644
--- a/spec/features/groups/empty_states_spec.rb
+++ b/spec/features/groups/empty_states_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Groups Merge Requests Empty States' do
+feature 'Group empty states' do
let(:group) { create(:group) }
let(:user) { create(:group_member, :developer, user: create(:user), group: group ).user }
@@ -8,62 +8,100 @@ feature 'Groups Merge Requests Empty States' do
sign_in(user)
end
- context 'group has a project' do
- let(:project) { create(:project, namespace: group) }
+ [:issue, :merge_request].each do |issuable|
+ issuable_name = issuable.to_s.humanize.downcase
+ project_relation = issuable == :issue ? :project : :source_project
- before do
- project.add_master(user)
- end
+ context "for #{issuable_name}s" do
+ let(:path) { public_send(:"#{issuable}s_group_path", group) }
- context 'the project has a merge request' do
- before do
- create(:merge_request, source_project: project)
+ context 'group has a project' do
+ let(:project) { create(:project, namespace: group) }
- visit merge_requests_group_path(group)
- end
+ before do
+ project.add_master(user)
+ end
- it 'should not display an empty state' do
- expect(page).not_to have_selector('.empty-state')
- end
- end
+ context "the project has #{issuable_name}s" do
+ before do
+ create(issuable, project_relation => project)
- context 'the project has no merge requests', :js do
- before do
- visit merge_requests_group_path(group)
- end
+ visit path
+ end
- it 'should display an empty state' do
- expect(page).to have_selector('.empty-state')
- end
+ it 'does not display an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
+
+ context "the project has no #{issuable_name}s", :js do
+ before do
+ visit path
+ end
+
+ it 'displays an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it "shows a new #{issuable_name} button" do
+ within '.empty-state' do
+ expect(page).to have_content("create #{issuable_name}")
+ end
+ end
+
+ it "the new #{issuable_name} button opens a project dropdown" do
+ within '.empty-state' do
+ find('.new-project-item-select-button').click
+ end
- it 'should show a new merge request button' do
- within '.empty-state' do
- expect(page).to have_content('create merge request')
+ expect(page).to have_selector('.ajax-project-dropdown')
+ end
end
end
- it 'the new merge request button opens a project dropdown' do
- within '.empty-state' do
- find('.new-project-item-select-button').click
- end
+ context 'group without a project' do
+ context 'group has a subgroup', :nested_groups do
+ let(:subgroup) { create(:group, parent: group) }
+ let(:subgroup_project) { create(:project, namespace: subgroup) }
- expect(page).to have_selector('.ajax-project-dropdown')
- end
- end
- end
+ context "the project has #{issuable_name}s" do
+ before do
+ create(issuable, project_relation => subgroup_project)
- context 'group without a project' do
- before do
- visit merge_requests_group_path(group)
- end
+ visit path
+ end
- it 'should display an empty state' do
- expect(page).to have_selector('.empty-state')
- end
+ it 'does not display an empty state' do
+ expect(page).not_to have_selector('.empty-state')
+ end
+ end
- it 'should not show a new merge request button' do
- within '.empty-state' do
- expect(page).not_to have_link('create merge request')
+ context "the project has no #{issuable_name}s" do
+ before do
+ visit path
+ end
+
+ it 'displays an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+ end
+ end
+
+ context 'group has no subgroups' do
+ before do
+ visit path
+ end
+
+ it 'displays an empty state' do
+ expect(page).to have_selector('.empty-state')
+ end
+
+ it "shows a new #{issuable_name} button" do
+ within '.empty-state' do
+ expect(page).not_to have_link("create #{issuable_name}")
+ end
+ end
+ end
end
end
end
diff --git a/spec/features/groups/members/search_members_spec.rb b/spec/features/groups/members/search_members_spec.rb
new file mode 100644
index 00000000000..31fbbcf562c
--- /dev/null
+++ b/spec/features/groups/members/search_members_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe 'Search group member' do
+ let(:user) { create :user }
+ let(:member) { create :user }
+
+ let!(:guest_group) do
+ create(:group) do |group|
+ group.add_guest(user)
+ group.add_guest(member)
+ end
+ end
+
+ before do
+ sign_in(user)
+ visit group_group_members_path(guest_group)
+ end
+
+ it 'renders member users' do
+ page.within '.member-search-form' do
+ fill_in 'search', with: member.name
+ find('.member-search-btn').click
+ end
+
+ group_members_list = find(".panel .content-list")
+ expect(group_members_list).to have_content(member.name)
+ expect(group_members_list).not_to have_content(user.name)
+ end
+end
diff --git a/spec/features/groups/milestone_spec.rb b/spec/features/groups/milestone_spec.rb
index 1b41b3842c8..20337f1d3b0 100644
--- a/spec/features/groups/milestone_spec.rb
+++ b/spec/features/groups/milestone_spec.rb
@@ -1,6 +1,6 @@
require 'rails_helper'
-feature 'Group milestones', :js do
+feature 'Group milestones' do
let(:group) { create(:group) }
let!(:project) { create(:project_empty_repo, group: group) }
let(:user) { create(:group_member, :master, user: create(:user), group: group ).user }
@@ -13,7 +13,7 @@ feature 'Group milestones', :js do
sign_in(user)
end
- context 'create a milestone' do
+ context 'create a milestone', :js do
before do
visit new_group_milestone_path(group)
end
@@ -61,55 +61,132 @@ feature 'Group milestones', :js do
end
context 'milestones list' do
- let!(:other_project) { create(:project_empty_repo, group: group) }
-
- let!(:active_project_milestone1) { create(:milestone, project: project, state: 'active', title: 'v1.0') }
- let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') }
- let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') }
- let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') }
- let!(:active_group_milestone) { create(:milestone, group: group, state: 'active') }
- let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') }
-
- before do
- visit group_milestones_path(group)
+ context 'when no milestones' do
+ it 'renders no milestones text' do
+ visit group_milestones_path(group)
+ expect(page).to have_content('No milestones to show')
+ end
end
- it 'counts milestones correctly' do
- expect(find('.top-area .active .badge').text).to eq("2")
- expect(find('.top-area .closed .badge').text).to eq("2")
- expect(find('.top-area .all .badge').text).to eq("4")
- end
+ context 'when milestones exists' do
+ let!(:other_project) { create(:project_empty_repo, group: group) }
+
+ let!(:active_project_milestone1) do
+ create(
+ :milestone,
+ project: project,
+ state: 'active',
+ title: 'v1.0',
+ due_date: '2114-08-20',
+ description: 'Lorem Ipsum is simply dummy text'
+ )
+ end
+ let!(:active_project_milestone2) { create(:milestone, project: other_project, state: 'active', title: 'v1.0') }
+ let!(:closed_project_milestone1) { create(:milestone, project: project, state: 'closed', title: 'v2.0') }
+ let!(:closed_project_milestone2) { create(:milestone, project: other_project, state: 'closed', title: 'v2.0') }
+ let!(:active_group_milestone) { create(:milestone, group: group, state: 'active', title: 'GL-113') }
+ let!(:closed_group_milestone) { create(:milestone, group: group, state: 'closed') }
+ let!(:issue) do
+ create :issue, project: project, assignees: [user], author: user, milestone: active_project_milestone1
+ end
- it 'lists legacy group milestones and group milestones' do
- legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first
+ before do
+ visit group_milestones_path(group)
+ end
- expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1)
- expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1)
- end
+ it 'counts milestones correctly' do
+ expect(find('.top-area .active .badge').text).to eq("2")
+ expect(find('.top-area .closed .badge').text).to eq("2")
+ expect(find('.top-area .all .badge').text).to eq("4")
+ end
- it 'updates milestone' do
- page.within(".milestones #milestone_#{active_group_milestone.id}") do
- click_link('Edit')
+ it 'lists legacy group milestones and group milestones' do
+ legacy_milestone = GroupMilestone.build_collection(group, group.projects, { state: 'active' }).first
+
+ expect(page).to have_selector("#milestone_#{active_group_milestone.id}", count: 1)
+ expect(page).to have_selector("#milestone_#{legacy_milestone.milestones.first.id}", count: 1)
end
- page.within('.milestone-form') do
- fill_in 'milestone_title', with: 'new title'
- click_button('Update milestone')
+ it 'updates milestone' do
+ page.within(".milestones #milestone_#{active_group_milestone.id}") do
+ click_link('Edit')
+ end
+
+ page.within('.milestone-form') do
+ fill_in 'milestone_title', with: 'new title'
+ click_button('Update milestone')
+ end
+
+ expect(find('#content-body h2')).to have_content('new title')
end
- expect(find('#content-body h2')).to have_content('new title')
- end
+ it 'shows milestone detail and supports its edit' do
+ page.within(".milestones #milestone_#{active_group_milestone.id}") do
+ click_link(active_group_milestone.title)
+ end
+
+ page.within('.detail-page-header') do
+ click_link('Edit')
+ end
- it 'shows milestone detail and supports its edit' do
- page.within(".milestones #milestone_#{active_group_milestone.id}") do
- click_link(active_group_milestone.title)
+ expect(page).to have_selector('.milestone-form')
end
- page.within('.detail-page-header') do
- click_link('Edit')
+ it 'renders milestones' do
+ expect(page).to have_content('v1.0')
+ expect(page).to have_content('GL-113')
+ expect(page).to have_link(
+ '1 Issue',
+ href: issues_group_path(group, milestone_title: 'v1.0')
+ )
+ expect(page).to have_link(
+ '0 Merge Requests',
+ href: merge_requests_group_path(group, milestone_title: 'v1.0')
+ )
end
- expect(page).to have_selector('.milestone-form')
+ it 'renders group milestone details' do
+ click_link 'v1.0'
+
+ expect(page).to have_content('expires on Aug 20, 2114')
+ expect(page).to have_content('v1.0')
+ expect(page).to have_content('Issues 1 Open: 1 Closed: 0')
+ expect(page).to have_link(issue.title, href: project_issue_path(issue.project, issue))
+ end
+
+ describe 'labels' do
+ before do
+ create(:label, project: project, title: 'bug') do |label|
+ issue.labels << label
+ end
+
+ create(:label, project: project, title: 'feature') do |label|
+ issue.labels << label
+ end
+ end
+
+ it 'renders labels' do
+ click_link 'v1.0'
+
+ page.within('#tab-issues') do
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'feature'
+ end
+ end
+
+ it 'renders labels list', :js do
+ click_link 'v1.0'
+
+ page.within('.content .nav-links') do
+ page.find(:xpath, "//a[@href='#tab-labels']").click
+ end
+
+ page.within('#tab-labels') do
+ expect(page).to have_content 'bug'
+ expect(page).to have_content 'feature'
+ end
+ end
+ end
end
end
end
diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb
index f355cec3ba9..41b9ada988a 100644
--- a/spec/features/issues/filtered_search/recent_searches_spec.rb
+++ b/spec/features/issues/filtered_search/recent_searches_spec.rb
@@ -39,8 +39,8 @@ describe 'Recent searches', :js do
items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
- expect(items[0].text).to eq('label:~qux garply')
- expect(items[1].text).to eq('label:~foo bar')
+ expect(items[0].text).to eq('label: ~qux garply')
+ expect(items[1].text).to eq('label: ~foo bar')
end
it 'saved recent searches are restored last on the list' do
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index faf14be4818..c2c4b479a8a 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -271,6 +271,18 @@ describe 'New/edit issue', :js do
end
end
+ context 'inline edit' do
+ before do
+ visit project_issue_path(project, issue)
+ end
+
+ it 'opens inline edit form with shortcut' do
+ find('body').send_keys('e')
+
+ expect(page).to have_selector('.detail-page-description form')
+ end
+ end
+
describe 'sub-group project' do
let(:group) { create(:group) }
let(:nested_group_1) { create(:group, parent: group) }
diff --git a/spec/features/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index f82ed6300cc..f82ed6300cc 100644
--- a/spec/features/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
diff --git a/spec/features/gitlab_flavored_markdown_spec.rb b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
index 3c2186b3598..3c2186b3598 100644
--- a/spec/features/gitlab_flavored_markdown_spec.rb
+++ b/spec/features/markdown/gitlab_flavored_markdown_spec.rb
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index f13d78d24e3..f13d78d24e3 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
diff --git a/spec/features/markdown/math_spec.rb b/spec/features/markdown/math_spec.rb
new file mode 100644
index 00000000000..6a23d6b78ab
--- /dev/null
+++ b/spec/features/markdown/math_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe 'Math rendering', :js do
+ it 'renders inline and display math correctly' do
+ description = <<~MATH
+ This math is inline $`a^2+b^2=c^2`$.
+
+ This is on a separate line
+ ```math
+ a^2+b^2=c^2
+ ```
+ MATH
+
+ project = create(:project, :public)
+ issue = create(:issue, project: project, description: description)
+
+ visit project_issue_path(project, issue)
+
+ expect(page).to have_selector('.katex .mord.mathit', text: 'b')
+ expect(page).to have_selector('.katex-display .mord.mathit', text: 'b')
+ end
+end
diff --git a/spec/features/markdown/mermaid_spec.rb b/spec/features/markdown/mermaid_spec.rb
new file mode 100644
index 00000000000..a25d701ee35
--- /dev/null
+++ b/spec/features/markdown/mermaid_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe 'Mermaid rendering', :js do
+ it 'renders Mermaid diagrams correctly' do
+ description = <<~MERMAID
+ ```mermaid
+ graph TD;
+ A-->B;
+ A-->C;
+ B-->D;
+ C-->D;
+ ```
+ MERMAID
+
+ project = create(:project, :public)
+ issue = create(:issue, project: project, description: description)
+
+ visit project_issue_path(project, issue)
+
+ %w[A B C D].each do |label|
+ expect(page).to have_selector('svg foreignObject', text: label)
+ end
+ end
+end
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index b02d2d4261c..cc12a1005ba 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -66,15 +66,16 @@ feature 'Milestone' do
end
end
- feature 'Open a milestone' do
+ feature 'Open a milestone', :js do
scenario 'shows total issue time spent correctly when no time has been logged' do
milestone = create(:milestone, project: project, title: 8.7)
visit project_milestone_path(project, milestone)
- page.within('.block.time_spent') do
- expect(page).to have_content 'No time spent'
- expect(page).to have_content 'None'
+ wait_for_requests
+
+ page.within('.time-tracking-no-tracking-pane') do
+ expect(page).to have_content 'No estimate or time spent'
end
end
@@ -89,8 +90,10 @@ feature 'Milestone' do
visit project_milestone_path(project, milestone)
- page.within('.block.time_spent') do
- expect(page).to have_content '3h'
+ wait_for_requests
+
+ page.within('.time-tracking-spend-only-pane') do
+ expect(page).to have_content 'Spent: 3h'
end
end
end
diff --git a/spec/features/profiles/password_spec.rb b/spec/features/profiles/password_spec.rb
index 4665626f114..1d7700b6767 100644
--- a/spec/features/profiles/password_spec.rb
+++ b/spec/features/profiles/password_spec.rb
@@ -1,6 +1,15 @@
require 'spec_helper'
describe 'Profile > Password' do
+ let(:user) { create(:user) }
+
+ def fill_passwords(password, confirmation)
+ fill_in 'New password', with: password
+ fill_in 'Password confirmation', with: confirmation
+
+ click_button 'Save password'
+ end
+
context 'Password authentication enabled' do
let(:user) { create(:user, password_automatically_set: true) }
@@ -9,13 +18,6 @@ describe 'Profile > Password' do
visit edit_profile_password_path
end
- def fill_passwords(password, confirmation)
- fill_in 'New password', with: password
- fill_in 'Password confirmation', with: confirmation
-
- click_button 'Save password'
- end
-
context 'User with password automatically set' do
describe 'User puts different passwords in the field and in the confirmation' do
it 'shows an error message' do
@@ -73,4 +75,64 @@ describe 'Profile > Password' do
end
end
end
+
+ context 'Change passowrd' do
+ before do
+ sign_in(user)
+ visit(edit_profile_password_path)
+ end
+
+ it 'does not change user passowrd without old one' do
+ page.within '.update-password' do
+ fill_passwords('22233344', '22233344')
+ end
+
+ page.within '.flash-container' do
+ expect(page).to have_content 'You must provide a valid current password'
+ end
+ end
+
+ it 'does not change password with invalid old password' do
+ page.within '.update-password' do
+ fill_in 'user_current_password', with: 'invalid'
+ fill_passwords('password', 'confirmation')
+ end
+
+ page.within '.flash-container' do
+ expect(page).to have_content 'You must provide a valid current password'
+ end
+ end
+
+ it 'changes user password' do
+ page.within '.update-password' do
+ fill_in "user_current_password", with: user.password
+ fill_passwords('22233344', '22233344')
+ end
+
+ expect(current_path).to eq new_user_session_path
+ end
+ end
+
+ context 'when password is expired' do
+ before do
+ sign_in(user)
+
+ user.update_attributes(password_expires_at: 1.hour.ago)
+ user.identities.delete
+ expect(user.ldap_user?).to eq false
+ end
+
+ it 'needs change user password' do
+ visit edit_profile_password_path
+
+ expect(current_path).to eq new_profile_password_path
+
+ fill_in :user_current_password, with: user.password
+ fill_in :user_password, with: '12345678'
+ fill_in :user_password_confirmation, with: '12345678'
+ click_button 'Set new password'
+
+ expect(current_path).to eq new_user_session_path
+ end
+ end
end
diff --git a/spec/features/profiles/user_edit_profile_spec.rb b/spec/features/profiles/user_edit_profile_spec.rb
new file mode 100644
index 00000000000..0b5eacbe916
--- /dev/null
+++ b/spec/features/profiles/user_edit_profile_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe 'User edit profile' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ visit(profile_path)
+ end
+
+ it 'changes user profile' do
+ fill_in 'user_skype', with: 'testskype'
+ fill_in 'user_linkedin', with: 'testlinkedin'
+ fill_in 'user_twitter', with: 'testtwitter'
+ fill_in 'user_website_url', with: 'testurl'
+ fill_in 'user_location', with: 'Ukraine'
+ fill_in 'user_bio', with: 'I <3 GitLab'
+ fill_in 'user_organization', with: 'GitLab'
+ click_button 'Update profile settings'
+
+ expect(user.reload).to have_attributes(
+ skype: 'testskype',
+ linkedin: 'testlinkedin',
+ twitter: 'testtwitter',
+ website_url: 'testurl',
+ bio: 'I <3 GitLab',
+ organization: 'GitLab'
+ )
+
+ expect(find('#user_location').value).to eq 'Ukraine'
+ expect(page).to have_content('Profile was successfully updated')
+ end
+
+ context 'user avatar' do
+ before do
+ attach_file(:user_avatar, Rails.root.join('spec', 'fixtures', 'banana_sample.gif'))
+ click_button 'Update profile settings'
+ end
+
+ it 'changes user avatar' do
+ expect(page).to have_link('Remove avatar')
+
+ user.reload
+ expect(user.avatar).to be_instance_of AvatarUploader
+ expect(user.avatar.url).to eq "/uploads/-/system/user/avatar/#{user.id}/banana_sample.gif"
+ end
+
+ it 'removes user avatar' do
+ click_link 'Remove avatar'
+
+ user.reload
+
+ expect(user.avatar?).to eq false
+ expect(page).not_to have_link('Remove avatar')
+ expect(page).to have_link('gravatar.com')
+ end
+ end
+end
diff --git a/spec/features/profiles/user_manages_applications_spec.rb b/spec/features/profiles/user_manages_applications_spec.rb
new file mode 100644
index 00000000000..387584fef62
--- /dev/null
+++ b/spec/features/profiles/user_manages_applications_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe 'User manages applications' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ visit applications_profile_path
+ end
+
+ it 'manages applications' do
+ expect(page).to have_content 'Add new application'
+
+ fill_in :doorkeeper_application_name, with: 'test'
+ fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com'
+ click_on 'Save application'
+
+ expect(page).to have_content 'Application: test'
+ expect(page).to have_content 'Application Id'
+ expect(page).to have_content 'Secret'
+
+ click_on 'Edit'
+
+ expect(page).to have_content 'Edit application'
+ fill_in :doorkeeper_application_name, with: 'test_changed'
+ click_on 'Save application'
+
+ expect(page).to have_content 'test_changed'
+ expect(page).to have_content 'Application Id'
+ expect(page).to have_content 'Secret'
+
+ visit applications_profile_path
+
+ page.within '.oauth-applications' do
+ click_on 'Destroy'
+ end
+ expect(page.find('.oauth-applications')).not_to have_content 'test_changed'
+ end
+end
diff --git a/spec/features/profiles/user_visits_profile_authentication_log_spec.rb b/spec/features/profiles/user_visits_profile_authentication_log_spec.rb
index a50ebb29e01..0f419c3c2c0 100644
--- a/spec/features/profiles/user_visits_profile_authentication_log_spec.rb
+++ b/spec/features/profiles/user_visits_profile_authentication_log_spec.rb
@@ -3,13 +3,28 @@ require 'spec_helper'
describe 'User visits the authentication log' do
let(:user) { create(:user) }
- before do
- sign_in(user)
+ context 'when user signed in' do
+ before do
+ sign_in(user)
+ end
- visit(audit_log_profile_path)
+ it 'shows correct menu item' do
+ visit(audit_log_profile_path)
+
+ expect(page).to have_active_navigation('Authentication log')
+ end
end
- it 'shows correct menu item' do
- expect(page).to have_active_navigation('Authentication log')
+ context 'when user has activity' do
+ before do
+ create(:closed_issue_event, author: user)
+ gitlab_sign_in(user)
+ end
+
+ it 'shows user activity' do
+ visit(audit_log_profile_path)
+
+ expect(page).to have_content 'Signed in with standard authentication'
+ end
end
end
diff --git a/spec/features/profiles/user_visits_profile_spec.rb b/spec/features/profiles/user_visits_profile_spec.rb
index a5d80439143..713112477c8 100644
--- a/spec/features/profiles/user_visits_profile_spec.rb
+++ b/spec/features/profiles/user_visits_profile_spec.rb
@@ -5,20 +5,58 @@ describe 'User visits their profile' do
before do
sign_in(user)
-
- visit(profile_path)
end
it 'shows correct menu item' do
+ visit(profile_path)
+
expect(page).to have_active_navigation('Profile')
end
- describe 'profile settings', :js do
- it 'saves updates' do
- fill_in 'user_bio', with: 'bio'
- click_button 'Update profile settings'
+ it 'shows profile info' do
+ visit(profile_path)
+
+ expect(page).to have_content "This information will appear on your profile"
+ end
+
+ context 'when user has groups' do
+ let(:group) do
+ create :group do |group|
+ group.add_owner(user)
+ end
+ end
+
+ let!(:project) do
+ create(:project, :repository, namespace: group) do |project|
+ create(:closed_issue_event, project: project)
+ project.add_master(user)
+ end
+ end
+
+ def click_on_profile_picture
+ find(:css, '.header-user-dropdown-toggle').click
+
+ page.within ".header-user" do
+ click_link "Profile"
+ end
+ end
+
+ it 'shows user groups', :js do
+ visit(profile_path)
+ click_on_profile_picture
+
+ page.within ".cover-block" do
+ expect(page).to have_content user.name
+ expect(page).to have_content user.username
+ end
+
+ page.within ".content" do
+ click_link "Groups"
+ end
- expect(page).to have_content('Profile was successfully updated')
+ page.within "#groups" do
+ expect(page).to have_content group.name
+ end
end
end
end
diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb
index 02dbd3380b3..4d47cdb500c 100644
--- a/spec/features/projects/clusters/gcp_spec.rb
+++ b/spec/features/projects/clusters/gcp_spec.rb
@@ -25,7 +25,7 @@ feature 'Gcp Cluster', :js do
context 'when user has a GCP project with billing enabled' do
before do
allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return('true')
+ allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(true)
end
context 'when user does not have a cluster and visits cluster index page' do
@@ -134,7 +134,7 @@ feature 'Gcp Cluster', :js do
context 'when user does not have a GCP project with billing enabled' do
before do
allow_any_instance_of(Projects::Clusters::GcpController).to receive(:authorize_google_project_billing)
- allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return('false')
+ allow_any_instance_of(Projects::Clusters::GcpController).to receive(:google_project_billing_status).and_return(false)
visit project_clusters_path(project)
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 8ac9821b879..7f1d1934103 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -11,7 +11,7 @@ feature 'project owner sees a link to create a license file in empty project', :
scenario 'project master creates a license file from a template' do
visit project_path(project)
- click_on 'LICENSE'
+ click_on 'Add License'
expect(page).to have_content('New file')
expect(current_path).to eq(
diff --git a/spec/features/projects/import_export/namespace_export_file_spec.rb b/spec/features/projects/import_export/namespace_export_file_spec.rb
index c1fccf4a40b..7d056b0c140 100644
--- a/spec/features/projects/import_export/namespace_export_file_spec.rb
+++ b/spec/features/projects/import_export/namespace_export_file_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-feature 'Import/Export - Namespace export file cleanup', :js do
+describe 'Import/Export - Namespace export file cleanup', :js do
let(:export_path) { Dir.mktmpdir('namespace_export_file_spec') }
before do
@@ -42,13 +42,13 @@ feature 'Import/Export - Namespace export file cleanup', :js do
end
describe 'legacy storage' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :legacy_storage) }
it_behaves_like 'handling project exports on namespace change'
end
describe 'hashed storage' do
- let(:project) { create(:project, :hashed) }
+ let(:project) { create(:project) }
it_behaves_like 'handling project exports on namespace change'
end
diff --git a/spec/features/projects/members/share_with_group_spec.rb b/spec/features/projects/members/share_with_group_spec.rb
index 4cf48098401..134c8b8bc39 100644
--- a/spec/features/projects/members/share_with_group_spec.rb
+++ b/spec/features/projects/members/share_with_group_spec.rb
@@ -149,6 +149,11 @@ feature 'Project > Members > Share with Group', :js do
create(:group).add_owner(master)
visit project_settings_members_path(project)
+
+ click_link 'Share with group'
+
+ find('.ajax-groups-select.select2-container')
+
execute_script 'GROUP_SELECT_PER_PAGE = 1;'
open_select2 '#link_group_id'
end
diff --git a/spec/features/projects/network_graph_spec.rb b/spec/features/projects/network_graph_spec.rb
new file mode 100644
index 00000000000..9f9a7787093
--- /dev/null
+++ b/spec/features/projects/network_graph_spec.rb
@@ -0,0 +1,108 @@
+require 'spec_helper'
+
+describe 'Project Network Graph', :js do
+ let(:user) { create :user }
+ let(:project) { create :project, :repository, namespace: user.namespace }
+
+ before do
+ sign_in(user)
+
+ # Stub Graph max_size to speed up test (10 commits vs. 650)
+ allow(Network::Graph).to receive(:max_count).and_return(10)
+ end
+
+ context 'when branch is master' do
+ def switch_ref_to(ref_name)
+ first('.js-project-refs-dropdown').click
+
+ page.within '.project-refs-form' do
+ click_link ref_name
+ end
+ end
+
+ def click_show_only_selected_branch_checkbox
+ find('#filter_ref').click
+ end
+
+ before do
+ visit project_network_path(project, 'master')
+ end
+
+ it 'renders project network' do
+ expect(page).to have_selector ".network-graph"
+ expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
+ page.within '.network-graph' do
+ expect(page).to have_content 'master'
+ end
+ end
+
+ it 'switches ref to branch' do
+ switch_ref_to('feature')
+
+ expect(page).to have_selector '.dropdown-menu-toggle', text: 'feature'
+ page.within '.network-graph' do
+ expect(page).to have_content 'feature'
+ end
+ end
+
+ it 'switches ref to tag' do
+ switch_ref_to('v1.0.0')
+
+ expect(page).to have_selector '.dropdown-menu-toggle', text: 'v1.0.0'
+ page.within '.network-graph' do
+ expect(page).to have_content 'v1.0.0'
+ end
+ end
+
+ it 'renders by commit sha of "v1.0.0"' do
+ page.within ".network-form" do
+ fill_in 'extended_sha1', with: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9'
+ find('button').click
+ end
+
+ expect(page).to have_selector ".network-graph"
+ expect(page).to have_selector '.dropdown-menu-toggle', text: "master"
+ page.within '.network-graph' do
+ expect(page).to have_content 'v1.0.0'
+ end
+ end
+
+ it 'filters select tag' do
+ switch_ref_to('v1.0.0')
+
+ expect(page).to have_css 'title', text: 'Graph · v1.0.0', visible: false
+ page.within '.network-graph' do
+ expect(page).to have_content 'Change some files'
+ end
+
+ click_show_only_selected_branch_checkbox
+
+ page.within '.network-graph' do
+ expect(page).not_to have_content 'Change some files'
+ end
+
+ click_show_only_selected_branch_checkbox
+
+ page.within '.network-graph' do
+ expect(page).to have_content 'Change some files'
+ end
+ end
+
+ it 'renders error message when sha commit not exists' do
+ page.within ".network-form" do
+ fill_in 'extended_sha1', with: ';'
+ find('button').click
+ end
+
+ expect(page).to have_selector '.flash-alert', text: "Git revision ';' does not exist."
+ end
+ end
+
+ it 'renders project network with test branch' do
+ visit project_network_path(project, "'test'")
+
+ page.within '.network-graph' do
+ expect(page).to have_content "'test'"
+ end
+ end
+end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index 6f097ad16c7..b5104747d00 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -140,7 +140,7 @@ feature 'New project' do
find('#import-project-tab').click
end
- context 'from git repository url' do
+ context 'from git repository url, "Repo by URL"' do
before do
first('.import_git').click
end
@@ -157,6 +157,18 @@ feature 'New project' do
expect(git_import_instructions).to be_visible
expect(git_import_instructions).to have_content 'Git repository URL'
end
+
+ it 'keeps "Import project" tab open after form validation error' do
+ collision_project = create(:project, name: 'test-name-collision', namespace: user.namespace)
+
+ fill_in 'project_import_url', with: collision_project.http_url_to_repo
+ fill_in 'project_path', with: collision_project.path
+
+ click_on 'Create project'
+
+ expect(page).to have_css('#import-project-pane.active')
+ expect(page).not_to have_css('.toggle-import-form.hide')
+ end
end
context 'from GitHub' do
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 2e334caa98f..a96f2c186a4 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -17,6 +17,7 @@ feature 'Pages' do
scenario '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
@@ -32,14 +33,161 @@ feature 'Pages' do
allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
end
- scenario 'sees "Remove pages" link' do
+ scenario 'renders Access pages' do
visit project_pages_path(project)
- expect(page).to have_link('Remove pages')
+ expect(page).to have_content('Access pages')
+ end
+
+ context 'when support for external domains is disabled' do
+ before do
+ allow(Gitlab.config.pages).to receive(:external_http).and_return(nil)
+ allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
+ end
+
+ it 'renders message that support is disabled' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Support for domains and certificates is disabled')
+ end
+ end
+
+ context 'when pages are exposed on external HTTP address' do
+ shared_examples 'adds new domain' do
+ it 'adds new domain' do
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
+ end
+
+ before do
+ allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80'])
+ allow(Gitlab.config.pages).to receive(:external_https).and_return(nil)
+ end
+
+ it 'allows to add new domain' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('New Domain')
+ end
+
+ it_behaves_like 'adds new domain'
+
+ context 'when project in group namespace' do
+ it_behaves_like 'adds new domain' do
+ let(:group) { create :group }
+ let(:project) { create :project, namespace: group }
+ end
+ end
+
+ context 'when pages domain is added' do
+ before do
+ project.pages_domains.create!(domain: 'my.test.domain.com')
+
+ visit new_project_pages_domain_path(project)
+ end
+
+ it 'renders certificates is disabled' do
+ expect(page).to have_content('Support for custom certificates is disabled')
+ end
+
+ it 'does not adds new domain and renders error message' do
+ fill_in 'Domain', with: 'my.test.domain.com'
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('Domain has already been taken')
+ end
+ end
+ end
+
+ context 'when pages are exposed on external HTTPS address' do
+ let(:certificate_pem) do
+ <<~PEM
+ -----BEGIN CERTIFICATE-----
+ MIICGzCCAYSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAbMRkwFwYDVQQDExB0ZXN0
+ LWNlcnRpZmljYXRlMB4XDTE2MDIxMjE0MzIwMFoXDTIwMDQxMjE0MzIwMFowGzEZ
+ MBcGA1UEAxMQdGVzdC1jZXJ0aWZpY2F0ZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+ gYkCgYEApL4J9L0ZxFJ1hI1LPIflAlAGvm6ZEvoT4qKU5Xf2JgU7/2geNR1qlNFa
+ SvCc08Knupp5yTgmvyK/Xi09U0N82vvp4Zvr/diSc4A/RA6Mta6egLySNT438kdT
+ nY2tR5feoTLwQpX0t4IMlwGQGT5h6Of2fKmDxzuwuyffcIHqLdsCAwEAAaNvMG0w
+ DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUxl9WSxBprB0z0ibJs3rXEk0+95AwCwYD
+ VR0PBAQDAgXgMBEGCWCGSAGG+EIBAQQEAwIGQDAeBglghkgBhvhCAQ0EERYPeGNh
+ IGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAGC4T8SlFHK0yPSa+idGLQFQ
+ joZp2JHYvNlTPkRJ/J4TcXxBTJmArcQgTIuNoBtC+0A/SwdK4MfTCUY4vNWNdese
+ 5A4K65Nb7Oh1AdQieTBHNXXCdyFsva9/ScfQGEl7p55a52jOPs0StPd7g64uvjlg
+ YHi2yesCrOvVXt+lgPTd
+ -----END CERTIFICATE-----
+ PEM
+ end
+
+ let(:certificate_key) do
+ <<~KEY
+ -----BEGIN PRIVATE KEY-----
+ MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKS+CfS9GcRSdYSN
+ SzyH5QJQBr5umRL6E+KilOV39iYFO/9oHjUdapTRWkrwnNPCp7qaeck4Jr8iv14t
+ PVNDfNr76eGb6/3YknOAP0QOjLWunoC8kjU+N/JHU52NrUeX3qEy8EKV9LeCDJcB
+ kBk+Yejn9nypg8c7sLsn33CB6i3bAgMBAAECgYA2D26w80T7WZvazYr86BNMePpd
+ j2mIAqx32KZHzt/lhh40J/SRtX9+Kl0Y7nBoRR5Ja9u/HkAIxNxLiUjwg9r6cpg/
+ uITEF5nMt7lAk391BuI+7VOZZGbJDsq2ulPd6lO+C8Kq/PI/e4kXcIjeH6KwQsuR
+ 5vrXfBZ3sQfflaiN4QJBANBt8JY2LIGQF8o89qwUpRL5vbnKQ4IzZ5+TOl4RLR7O
+ AQpJ81tGuINghO7aunctb6rrcKJrxmEH1whzComybrMCQQDKV49nOBudRBAIgG4K
+ EnLzsRKISUHMZSJiYTYnablof8cKw1JaQduw7zgrUlLwnroSaAGX88+Jw1f5n2Lh
+ Vlg5AkBDdUGnrDLtYBCDEQYZHblrkc7ZAeCllDOWjxUV+uMqlCv8A4Ey6omvY57C
+ m6I8DkWVAQx8VPtozhvHjUw80rZHAkB55HWHAM3h13axKG0htCt7klhPsZHpx6MH
+ EPjGlXIT+aW2XiPmK3ZlCDcWIenE+lmtbOpI159Wpk8BGXs/s/xBAkEAlAY3ymgx
+ 63BDJEwvOb2IaP8lDDxNsXx9XJNVvQbv5n15vNsLHbjslHfAhAbxnLQ1fLhUPqSi
+ nNp/xedE1YxutQ==
+ -----END PRIVATE KEY-----
+ KEY
+ end
+
+ before do
+ allow(Gitlab.config.pages).to receive(:external_http).and_return(['1.1.1.1:80'])
+ allow(Gitlab.config.pages).to receive(:external_https).and_return(['1.1.1.1:443'])
+ end
+
+ it 'adds new domain with certificate' do
+ visit new_project_pages_domain_path(project)
+
+ fill_in 'Domain', with: 'my.test.domain.com'
+ fill_in 'Certificate (PEM)', with: certificate_pem
+ fill_in 'Key (PEM)', with: certificate_key
+ click_button 'Create New Domain'
+
+ expect(page).to have_content('my.test.domain.com')
+ end
end
end
it_behaves_like 'no pages deployed'
+
+ describe 'project settings page' do
+ it 'renders "Pages" tab' do
+ visit edit_project_path(project)
+
+ page.within '.nav-sidebar' do
+ expect(page).to have_link('Pages')
+ end
+ end
+
+ context 'when pages are disabled' do
+ before do
+ allow(Gitlab.config.pages).to receive(:enabled).and_return(false)
+ end
+
+ it 'does not render "Pages" tab' do
+ visit edit_project_path(project)
+
+ page.within '.nav-sidebar' do
+ expect(page).not_to have_link('Pages')
+ end
+ end
+ end
+ end
end
context 'when the user is not the owner' do
@@ -57,4 +205,54 @@ feature 'Pages' do
it_behaves_like 'no pages deployed'
end
+
+ describe 'Remove page' do
+ context 'when user is the owner' do
+ let(:project) { create :project, :repository }
+
+ before do
+ project.namespace.update(owner: user)
+ end
+
+ context 'when pages are deployed' do
+ let(:pipeline) do
+ commit_sha = project.commit('HEAD').sha
+
+ project.pipelines.create(
+ ref: 'HEAD',
+ sha: commit_sha,
+ source: :push,
+ protected: false
+ )
+ end
+
+ let(:ci_build) do
+ build(
+ :ci_build,
+ project: project,
+ pipeline: pipeline,
+ ref: 'HEAD',
+ legacy_artifacts_file: fixture_file_upload(Rails.root.join('spec/fixtures/pages.zip')),
+ legacy_artifacts_metadata: fixture_file_upload(Rails.root.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
+
+ it 'removes the pages' do
+ visit project_pages_path(project)
+
+ expect(page).to have_link('Remove pages')
+
+ click_link 'Remove pages'
+
+ expect(project.pages_deployed?).to be_falsey
+ end
+ end
+ end
+ end
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
new file mode 100644
index 00000000000..e9502178bd7
--- /dev/null
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -0,0 +1,92 @@
+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"
+ fill_in 'service_new_issue_url', with: url
+ end
+
+ before do
+ project.add_master(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: 'Redmine'
+ 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_jira_spec.rb b/spec/features/projects/services/user_activates_jira_spec.rb
index 028669eeaf2..429128ec096 100644
--- a/spec/features/projects/services/user_activates_jira_spec.rb
+++ b/spec/features/projects/services/user_activates_jira_spec.rb
@@ -3,7 +3,6 @@ require 'spec_helper'
describe 'User activates Jira', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:service) { project.create_jira_service }
let(:url) { 'http://jira.example.com' }
let(:test_url) { 'http://jira.example.com/rest/api/2/serverInfo' }
@@ -26,7 +25,7 @@ describe 'User activates Jira', :js do
describe 'user sets and activates Jira Service' do
context 'when Jira connection test succeeds' do
- it 'activates the JIRA service' do
+ before do
server_info = { key: 'value' }.to_json
WebMock.stub_request(:get, test_url).with(basic_auth: %w(username password)).to_return(body: server_info)
@@ -34,10 +33,18 @@ describe 'User activates Jira', :js do
fill_form
click_button('Test settings and save changes')
wait_for_requests
+ end
+ it 'activates the JIRA service' do
expect(page).to have_content('JIRA activated.')
expect(current_path).to eq(project_settings_integrations_path(project))
end
+
+ it 'shows the JIRA link in the menu' do
+ page.within('.nav-sidebar') do
+ expect(page).to have_link('JIRA', href: url)
+ end
+ end
end
context 'when Jira connection test fails' do
@@ -75,14 +82,20 @@ describe 'User activates Jira', :js do
end
describe 'user sets Jira Service but keeps it disabled' do
- context 'when Jira connection test succeeds' do
- it 'activates the JIRA service' do
- click_link('JIRA')
- fill_form(false)
- click_button('Save changes')
+ before do
+ click_link('JIRA')
+ fill_form(false)
+ click_button('Save changes')
+ end
- expect(page).to have_content('JIRA settings saved, but not activated.')
- expect(current_path).to eq(project_settings_integrations_path(project))
+ it 'saves but does not activate the JIRA service' do
+ expect(page).to have_content('JIRA settings saved, but not activated.')
+ expect(current_path).to eq(project_settings_integrations_path(project))
+ end
+
+ it 'does not show the JIRA link in the menu' do
+ page.within('.nav-sidebar') do
+ expect(page).not_to have_link('JIRA', href: url)
end
end
end
diff --git a/spec/features/projects/services/user_activates_prometheus_spec.rb b/spec/features/projects/services/user_activates_prometheus_spec.rb
new file mode 100644
index 00000000000..33f884eb148
--- /dev/null
+++ b/spec/features/projects/services/user_activates_prometheus_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe 'User activates Prometheus' do
+ let(:project) { create(:project) }
+ let(:user) { create(:user) }
+
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit(project_settings_integrations_path(project))
+
+ click_link('Prometheus')
+ end
+
+ it 'activates service' do
+ check('Active')
+ fill_in('API URL', with: 'http://prometheus.example.com')
+ click_button('Save changes')
+
+ expect(page).to have_content('Prometheus activated.')
+ end
+end
diff --git a/spec/features/projects/show_project_spec.rb b/spec/features/projects/show_project_spec.rb
index 0b94c9eae5d..0a014e9f080 100644
--- a/spec/features/projects/show_project_spec.rb
+++ b/spec/features/projects/show_project_spec.rb
@@ -17,4 +17,321 @@ describe 'Project show page', :feature do
expect(page).to have_content("This project was scheduled for deletion, but failed with the following message: #{project.delete_error}")
end
end
+
+ describe 'stat button existence' do
+ # For "New file", "Add License" functionality,
+ # see spec/features/projects/files/project_owner_creates_license_file_spec.rb
+ # see spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+
+ let(:user) { create(:user) }
+
+ describe 'empty project' do
+ let(:project) { create(:project, :public, :empty_repo) }
+ let(:presenter) { project.present(current_user: user) }
+
+ describe 'as a normal user' do
+ before do
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'no Auto DevOps button if can not manage pipelines' do
+ page.within('.project-stats') 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
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_text('Auto DevOps enabled')
+ end
+ end
+ end
+
+ describe 'as a master' do
+ before do
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it '"New file" button linked to new file page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('New file', href: project_new_blob_path(project, project.default_branch || 'master'))
+ end
+ end
+
+ it '"Add Readme" button linked to new file populated for a readme' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Add Readme', href: presenter.add_readme_path)
+ end
+ end
+
+ it '"Add License" button linked to new file populated for a license' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Add License', href: presenter.add_license_path)
+ end
+ end
+
+ describe 'Auto DevOps button' do
+ it '"Enable Auto DevOps" button linked to settings page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+
+ it '"Auto DevOps enabled" anchor linked to settings page' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+ end
+
+ describe 'Kubernetes cluster button' do
+ it '"Add Kubernetes cluster" button linked to clusters page' do
+ page.within('.project-stats') 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-stats') do
+ expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
+ end
+ end
+ end
+ end
+ end
+
+ describe 'populated project' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:presenter) { project.present(current_user: user) }
+
+ describe 'as a normal user' do
+ before do
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'no Auto DevOps button if can not manage pipelines' do
+ page.within('.project-stats') 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
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_text('Auto DevOps enabled')
+ end
+ end
+
+ it 'no Kubernetes cluster button if can not manage clusters' do
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Kubernetes cluster')
+ expect(page).not_to have_link('Kubernetes configured')
+ end
+ end
+ end
+
+ describe 'as a master' do
+ before do
+ allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(false)
+ project.add_master(user)
+ sign_in(user)
+
+ visit project_path(project)
+ end
+
+ it 'no "Add Changelog" button if the project already has a changelog' do
+ expect(project.repository.changelog).not_to be_nil
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Changelog')
+ end
+ end
+
+ it 'no "Add License" button if the project already has a license' do
+ expect(project.repository.license_blob).not_to be_nil
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add License')
+ end
+ end
+
+ it 'no "Add Contribution guide" button if the project already has a contribution guide' do
+ expect(project.repository.contribution_guide).not_to be_nil
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Add Contribution guide')
+ end
+ end
+
+ describe 'GitLab CI configuration button' do
+ it '"Set up CI/CD" button linked to new file populated for a .gitlab-ci.yml' do
+ expect(project.repository.gitlab_ci_yml).to be_nil
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Set up CI/CD', href: presenter.add_ci_yml_path)
+ end
+ end
+
+ it 'no "Set up CI/CD" button if the project already has a .gitlab-ci.yml' do
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
+
+ expect(project.repository.gitlab_ci_yml).not_to be_nil
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up CI/CD')
+ end
+ end
+
+ it 'no "Set up CI/CD" button if the project has Auto DevOps enabled' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up CI/CD')
+ end
+ end
+ end
+
+ describe 'Auto DevOps button' do
+ it '"Enable Auto DevOps" button linked to settings page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+
+ it '"Enable Auto DevOps" button linked to settings page' do
+ project.create_auto_devops!(enabled: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings'))
+ end
+ end
+
+ it 'no Auto DevOps button if Auto DevOps callout is shown' do
+ allow_any_instance_of(AutoDevopsHelper).to receive(:show_auto_devops_callout?).and_return(true)
+
+ visit project_path(project)
+
+ expect(page).to have_selector('.js-autodevops-banner')
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+
+ it 'no "Enable Auto DevOps" button when .gitlab-ci.yml already exists' do
+ Files::CreateService.new(
+ project,
+ project.creator,
+ start_branch: 'master',
+ branch_name: 'master',
+ commit_message: "Add .gitlab-ci.yml",
+ file_path: '.gitlab-ci.yml',
+ file_content: File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
+ ).execute
+
+ expect(project.repository.gitlab_ci_yml).not_to be_nil
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ end
+ end
+ end
+
+ describe 'Kubernetes cluster button' do
+ it '"Add Kubernetes cluster" button linked to clusters page' do
+ page.within('.project-stats') do
+ expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
+ end
+ end
+
+ it '"Kubernetes cluster" button linked to cluster page' do
+ cluster = create(:cluster, :provided_by_gcp, projects: [project])
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
+ end
+ end
+ end
+
+ describe '"Set up Koding" button' do
+ it 'no "Set up Koding" button if Koding disabled' do
+ stub_application_setting(koding_enabled?: false)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up Koding')
+ end
+ end
+
+ it 'no "Set up Koding" button if the project already has a .koding.yml' do
+ stub_application_setting(koding_enabled?: true)
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:koding_url).and_return('http://koding.example.com')
+ expect(project.repository.changelog).not_to be_nil
+ allow_any_instance_of(Repository).to receive(:koding_yml).and_return(project.repository.changelog)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).not_to have_link('Set up Koding')
+ end
+ end
+
+ it '"Set up Koding" button linked to new file populated for a .koding.yml' do
+ stub_application_setting(koding_enabled?: true)
+
+ visit project_path(project)
+
+ page.within('.project-stats') do
+ expect(page).to have_link('Set up Koding', href: presenter.add_koding_stack_path)
+ end
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb
index b66a7dea598..cfe979a8647 100644
--- a/spec/features/projects_spec.rb
+++ b/spec/features/projects_spec.rb
@@ -25,6 +25,24 @@ feature 'Project' do
end
end
+ describe 'shows tip about push to create git command' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in user
+ visit new_project_path
+ end
+
+ it 'shows the command in a popover', :js do
+ page.within '.profile-settings-sidebar' do
+ click_link 'Show command'
+ end
+
+ expect(page).to have_css('.popover .push-to-create-popover #push_to_create_tip')
+ expect(page).to have_content 'Private projects can be created in your personal namespace with:'
+ end
+ end
+
describe 'description' do
let(:project) { create(:project, :repository) }
let(:path) { project_path(project) }
@@ -128,8 +146,8 @@ feature 'Project' do
end
describe 'removal', :js do
- let(:user) { create(:user, username: 'test', name: 'test') }
- let(:project) { create(:project, namespace: user.namespace, name: 'project1') }
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
before do
sign_in(user)
@@ -138,8 +156,8 @@ feature 'Project' do
end
it 'removes a project' do
- expect { remove_with_confirm('Remove project', project.path) }.to change {Project.count}.by(-1)
- expect(page).to have_content "Project 'test / project1' is in the process of being deleted."
+ expect { remove_with_confirm('Remove project', project.path) }.to change { Project.count }.by(-1)
+ expect(page).to have_content "Project '#{project.full_name}' is in the process of being deleted."
expect(Project.all.count).to be_zero
expect(project.issues).to be_empty
expect(project.merge_requests).to be_empty
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index aec9de6c7ca..df65c2d2f83 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -7,6 +7,20 @@ feature 'Runners' do
sign_in(user)
end
+ context 'when user opens runners page' do
+ given(:project) { create(:project) }
+
+ background do
+ project.add_master(user)
+ end
+
+ scenario 'user can see a button to install runners on kubernetes clusters' do
+ visit runners_path(project)
+
+ expect(page).to have_link('Install Runner on Kubernetes', href: project_clusters_path(project))
+ end
+ end
+
context 'when a project has enabled shared_runners' do
given(:project) { create(:project) }
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
deleted file mode 100644
index 917fad74ef1..00000000000
--- a/spec/features/signup_spec.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-require 'spec_helper'
-
-feature 'Signup' do
- describe 'signup with no errors' do
- context "when sending confirmation email" do
- before do
- stub_application_setting(send_user_confirmation_email: true)
- end
-
- it 'creates the user account and sends a confirmation email' do
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq users_almost_there_path
- expect(page).to have_content("Please check your email to confirm your account")
- end
- end
-
- context "when sigining up with different cased emails" do
- it "creates the user successfully" do
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email.capitalize
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Welcome! You have signed up successfully.")
- end
- end
-
- context "when not sending confirmation email" do
- before do
- stub_application_setting(send_user_confirmation_email: false)
- end
-
- it 'creates the user account and goes to dashboard' do
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq dashboard_projects_path
- expect(page).to have_content("Welcome! You have signed up successfully.")
- end
- end
- end
-
- describe 'signup with errors' do
- it "displays the errors" do
- existing_user = create(:user)
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: existing_user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq user_registration_path
- expect(page).to have_content("errors prohibited this user from being saved")
- expect(page).to have_content("Email has already been taken")
- expect(page).to have_content("Email confirmation doesn't match")
- end
-
- it 'does not redisplay the password' do
- existing_user = create(:user)
- user = build(:user)
-
- visit root_path
-
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: existing_user.email
- fill_in 'new_user_password', with: user.password
- click_button "Register"
-
- expect(current_path).to eq user_registration_path
- expect(page.body).not_to match(/#{user.password}/)
- end
- end
-end
diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb
index 4662367d843..b625e7065cc 100644
--- a/spec/features/tags/master_views_tags_spec.rb
+++ b/spec/features/tags/master_views_tags_spec.rb
@@ -13,7 +13,7 @@ feature 'Master views tags' do
before do
visit project_path(project)
- click_on 'README'
+ click_on 'Add Readme'
fill_in :commit_message, with: 'Add a README file', visible: true
click_button 'Commit changes'
visit project_tags_path(project)
diff --git a/spec/features/login_spec.rb b/spec/features/users/login_spec.rb
index 6dfabcc7225..6ef235cf870 100644
--- a/spec/features/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -1,6 +1,26 @@
require 'spec_helper'
feature 'Login' do
+ scenario 'Successful user signin invalidates password reset token' do
+ user = create(:user)
+
+ expect(user.reset_password_token).to be_nil
+
+ visit new_user_password_path
+ fill_in 'user_email', with: user.email
+ click_button 'Reset password'
+
+ user.reload
+ expect(user.reset_password_token).not_to be_nil
+
+ find('a[href="#login-pane"]').click
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
+
+ user.reload
+ expect(user.reset_password_token).to be_nil
+ end
+
describe 'initial login after setup' do
it 'allows the initial admin to create a password' do
# This behavior is dependent on there only being one user
diff --git a/spec/features/logout_spec.rb b/spec/features/users/logout_spec.rb
index 635729efa53..635729efa53 100644
--- a/spec/features/logout_spec.rb
+++ b/spec/features/users/logout_spec.rb
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
deleted file mode 100644
index f079771cee1..00000000000
--- a/spec/features/users/projects_spec.rb
+++ /dev/null
@@ -1,29 +0,0 @@
-require 'spec_helper'
-
-describe 'Projects tab on a user profile', :js do
- let(:user) { create(:user) }
- let!(:project) { create(:project, namespace: user.namespace) }
- let!(:project2) { create(:project, namespace: user.namespace) }
-
- before do
- allow(Project).to receive(:default_per_page).and_return(1)
-
- sign_in(user)
-
- visit user_path(user)
-
- page.within('.user-profile-nav') do
- click_link('Personal projects')
- end
-
- wait_for_requests
- end
-
- it 'paginates results' do
- expect(page).to have_content(project2.name)
-
- click_link('Next')
-
- expect(page).to have_content(project.name)
- end
-end
diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb
new file mode 100644
index 00000000000..b5bbb2c0ea5
--- /dev/null
+++ b/spec/features/users/show_spec.rb
@@ -0,0 +1,17 @@
+require 'spec_helper'
+
+describe 'User page' do
+ let(:user) { create(:user) }
+
+ it 'shows all the tabs' do
+ visit(user_path(user))
+
+ page.within '.nav-links' do
+ expect(page).to have_link('Activity')
+ expect(page).to have_link('Groups')
+ expect(page).to have_link('Contributed projects')
+ expect(page).to have_link('Personal projects')
+ expect(page).to have_link('Snippets')
+ end
+ end
+end
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
new file mode 100644
index 00000000000..5d539f0ccbe
--- /dev/null
+++ b/spec/features/users/signup_spec.rb
@@ -0,0 +1,135 @@
+require 'spec_helper'
+
+describe 'Signup' do
+ let(:new_user) { build_stubbed(:user) }
+
+ describe 'username validation', :js do
+ before do
+ visit root_path
+ click_link 'Register'
+ end
+
+ it 'does not show an error border if the username is available' do
+ fill_in 'new_user_username', with: 'new-user'
+ wait_for_requests
+
+ expect(find('.username')).not_to have_css '.gl-field-error-outline'
+ end
+
+ it 'does not show an error border if the username contains dots (.)' do
+ fill_in 'new_user_username', with: 'new.user.username'
+ wait_for_requests
+
+ expect(find('.username')).not_to have_css '.gl-field-error-outline'
+ end
+
+ it 'shows an error border if the username already exists' do
+ existing_user = create(:user)
+
+ fill_in 'new_user_username', with: existing_user.username
+ wait_for_requests
+
+ expect(find('.username')).to have_css '.gl-field-error-outline'
+ end
+
+ it 'shows an error border if the username contains special characters' do
+ fill_in 'new_user_username', with: 'new$user!username'
+ wait_for_requests
+
+ expect(find('.username')).to have_css '.gl-field-error-outline'
+ end
+ end
+
+ context 'with no errors' do
+ context "when sending confirmation email" do
+ before do
+ stub_application_setting(send_user_confirmation_email: true)
+ end
+
+ it 'creates the user account and sends a confirmation email' do
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_password', with: new_user.password
+
+ expect { click_button 'Register' }.to change { User.count }.by(1)
+
+ expect(current_path).to eq users_almost_there_path
+ expect(page).to have_content("Please check your email to confirm your account")
+ end
+ end
+
+ context "when sigining up with different cased emails" do
+ it "creates the user successfully" do
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email.capitalize
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq dashboard_projects_path
+ expect(page).to have_content("Welcome! You have signed up successfully.")
+ end
+ end
+
+ context "when not sending confirmation email" do
+ before do
+ stub_application_setting(send_user_confirmation_email: false)
+ end
+
+ it 'creates the user account and goes to dashboard' do
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: new_user.email
+ fill_in 'new_user_email_confirmation', with: new_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq dashboard_projects_path
+ expect(page).to have_content("Welcome! You have signed up successfully.")
+ end
+ end
+ end
+
+ context 'with errors' do
+ it "displays the errors" do
+ existing_user = create(:user)
+
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: existing_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq user_registration_path
+ expect(page).to have_content("errors prohibited this user from being saved")
+ expect(page).to have_content("Email has already been taken")
+ expect(page).to have_content("Email confirmation doesn't match")
+ end
+
+ it 'does not redisplay the password' do
+ existing_user = create(:user)
+
+ visit root_path
+
+ fill_in 'new_user_name', with: new_user.name
+ fill_in 'new_user_username', with: new_user.username
+ fill_in 'new_user_email', with: existing_user.email
+ fill_in 'new_user_password', with: new_user.password
+ click_button "Register"
+
+ expect(current_path).to eq user_registration_path
+ expect(page.body).not_to match(/#{new_user.password}/)
+ end
+ end
+end
diff --git a/spec/features/user_page_spec.rb b/spec/features/users/user_browses_projects_on_user_page_spec.rb
index 19c587e53c8..a70637c8370 100644
--- a/spec/features/user_page_spec.rb
+++ b/spec/features/users/user_browses_projects_on_user_page_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'User page', :js do
+describe 'Users > User browses projects on user page', :js do
let!(:user) { create :user }
let!(:private_project) do
create :project, :private, name: 'private', namespace: user.namespace do |project|
@@ -26,6 +26,28 @@ describe 'User page', :js do
end
end
+ it 'paginates projects', :js do
+ project = create(:project, namespace: user.namespace)
+ project2 = create(:project, namespace: user.namespace)
+ allow(Project).to receive(:default_per_page).and_return(1)
+
+ sign_in(user)
+
+ visit user_path(user)
+
+ page.within('.user-profile-nav') do
+ click_link('Personal projects')
+ end
+
+ wait_for_requests
+
+ expect(page).to have_content(project2.name)
+
+ click_link('Next')
+
+ expect(page).to have_content(project.name)
+ end
+
context 'when not signed in' do
it 'renders user public project' do
visit user_path(user)
diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb
deleted file mode 100644
index a9973cdf214..00000000000
--- a/spec/features/users_spec.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-require 'spec_helper'
-
-feature 'Users', :js do
- let(:user) { create(:user, username: 'user1', name: 'User 1', email: 'user1@gitlab.com') }
-
- scenario 'GET /users/sign_in creates a new user account' do
- visit new_user_session_path
- click_link 'Register'
- fill_in 'new_user_name', with: 'Name Surname'
- fill_in 'new_user_username', with: 'Great'
- fill_in 'new_user_email', with: 'name@mail.com'
- fill_in 'new_user_email_confirmation', with: 'name@mail.com'
- fill_in 'new_user_password', with: 'password1234'
- expect { click_button 'Register' }.to change { User.count }.by(1)
- end
-
- scenario 'Successful user signin invalidates password reset token' do
- expect(user.reset_password_token).to be_nil
-
- visit new_user_password_path
- fill_in 'user_email', with: user.email
- click_button 'Reset password'
-
- user.reload
- expect(user.reset_password_token).not_to be_nil
-
- find('a[href="#login-pane"]').click
- gitlab_sign_in(user)
- expect(current_path).to eq root_path
-
- user.reload
- expect(user.reset_password_token).to be_nil
- end
-
- scenario 'Should show one error if email is already taken' do
- visit new_user_session_path
- click_link 'Register'
- fill_in 'new_user_name', with: 'Another user name'
- fill_in 'new_user_username', with: 'anotheruser'
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_email_confirmation', with: user.email
- fill_in 'new_user_password', with: '12341234'
- expect { click_button 'Register' }.to change { User.count }.by(0)
- expect(page).to have_text('Email has already been taken')
- expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}'
- end
-
- describe 'redirect alias routes' do
- before do
- expect(user).to be_persisted
- end
-
- scenario '/u/user1 redirects to user page' do
- visit '/u/user1'
-
- expect(current_path).to eq user_path(user)
- expect(page).to have_text(user.name)
- end
-
- scenario '/u/user1/groups redirects to user groups page' do
- visit '/u/user1/groups'
-
- expect(current_path).to eq user_groups_path(user)
- end
-
- scenario '/u/user1/projects redirects to user projects page' do
- visit '/u/user1/projects'
-
- expect(current_path).to eq user_projects_path(user)
- end
- end
-
- feature 'username validation' do
- let(:loading_icon) { '.fa.fa-spinner' }
- let(:username_input) { 'new_user_username' }
-
- before do
- visit new_user_session_path
- click_link 'Register'
- end
-
- scenario 'doesn\'t show an error border if the username is available' do
- fill_in username_input, with: 'new-user'
- wait_for_requests
- expect(find('.username')).not_to have_css '.gl-field-error-outline'
- end
-
- scenario 'does not show an error border if the username contains dots (.)' do
- fill_in username_input, with: 'new.user.username'
- wait_for_requests
- expect(find('.username')).not_to have_css '.gl-field-error-outline'
- end
-
- scenario 'shows an error border if the username already exists' do
- fill_in username_input, with: user.username
- wait_for_requests
- expect(find('.username')).to have_css '.gl-field-error-outline'
- end
-
- scenario 'shows an error border if the username contains special characters' do
- fill_in username_input, with: 'new$user!username'
- wait_for_requests
- expect(find('.username')).to have_css '.gl-field-error-outline'
- end
- end
-
- def errors_on_page(page)
- page.find('#error_explanation').find('ul').all('li').map { |item| item.text }.join("\n")
- end
-
- def number_of_errors_on_page(page)
- page.find('#error_explanation').find('ul').all('li').count
- end
-end
diff --git a/spec/finders/concerns/finder_methods_spec.rb b/spec/finders/concerns/finder_methods_spec.rb
new file mode 100644
index 00000000000..a4ad331f613
--- /dev/null
+++ b/spec/finders/concerns/finder_methods_spec.rb
@@ -0,0 +1,70 @@
+require 'spec_helper'
+
+describe FinderMethods do
+ let(:finder_class) do
+ Class.new do
+ include FinderMethods
+
+ attr_reader :current_user
+
+ def initialize(user)
+ @current_user = user
+ end
+
+ def execute
+ Project.all
+ end
+ end
+ end
+
+ let(:user) { create(:user) }
+ let(:finder) { finder_class.new(user) }
+ let(:authorized_project) { create(:project) }
+ let(:unauthorized_project) { create(:project) }
+
+ before do
+ authorized_project.add_developer(user)
+ end
+
+ describe '#find_by!' do
+ it 'returns the project if the user has access' do
+ expect(finder.find_by!(id: authorized_project.id)).to eq(authorized_project)
+ end
+
+ it 'raises not found when the project is not found' do
+ expect { finder.find_by!(id: 0) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ 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
+ end
+
+ describe '#find' do
+ it 'returns the project if the user has access' do
+ expect(finder.find(authorized_project.id)).to eq(authorized_project)
+ end
+
+ it 'raises not found when the project is not found' do
+ expect { finder.find(0) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'raises not found the user does not have access' do
+ expect { finder.find(unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+ end
+
+ describe '#find_by' do
+ it 'returns the project if the user has access' do
+ expect(finder.find_by(id: authorized_project.id)).to eq(authorized_project)
+ end
+
+ it 'returns nil when the project is not found' do
+ expect(finder.find_by(id: 0)).to be_nil
+ end
+
+ it 'returns nil when the user does not have access' do
+ expect(finder.find_by(id: unauthorized_project.id)).to be_nil
+ end
+ end
+end
diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
new file mode 100644
index 00000000000..c784fb87972
--- /dev/null
+++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
@@ -0,0 +1,118 @@
+require 'spec_helper'
+
+describe FinderWithCrossProjectAccess do
+ let(:finder_class) do
+ Class.new do
+ prepend FinderWithCrossProjectAccess
+ include FinderMethods
+
+ requires_cross_project_access if: -> { requires_access? }
+
+ attr_reader :current_user
+
+ def initialize(user)
+ @current_user = user
+ end
+
+ def execute
+ Issue.all
+ end
+ end
+ end
+
+ let(:user) { create(:user) }
+ subject(:finder) { finder_class.new(user) }
+ let!(:result) { create(:issue) }
+
+ before do
+ result.project.add_master(user)
+ end
+
+ def expect_access_check_on_result
+ expect(finder).not_to receive(:requires_access?)
+ expect(Ability).to receive(:allowed?).with(user, :read_issue, result).and_call_original
+ end
+
+ context 'when the user cannot read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project)
+ .and_return(false)
+ end
+
+ describe '#execute' do
+ it 'returns a issue if the check is disabled' do
+ expect(finder).to receive(:requires_access?).and_return(false)
+
+ expect(finder.execute).to include(result)
+ end
+
+ it 'returns an empty relation when the check is enabled' do
+ expect(finder).to receive(:requires_access?).and_return(true)
+
+ expect(finder.execute).to be_empty
+ end
+
+ it 'only queries once when check is enabled' do
+ expect(finder).to receive(:requires_access?).and_return(true)
+
+ expect { finder.execute }.not_to exceed_query_limit(1)
+ end
+
+ it 'only queries once when check is disabled' do
+ expect(finder).to receive(:requires_access?).and_return(false)
+
+ expect { finder.execute }.not_to exceed_query_limit(1)
+ end
+ end
+
+ describe '#find' do
+ it 'checks the accessibility of the subject directly' do
+ expect_access_check_on_result
+
+ finder.find(result.id)
+ end
+
+ it 'returns the issue' do
+ expect(finder.find(result.id)).to eq(result)
+ end
+ end
+
+ describe '#find_by' do
+ it 'checks the accessibility of the subject directly' do
+ expect_access_check_on_result
+
+ finder.find_by(id: result.id)
+ end
+ end
+
+ describe '#find_by!' do
+ it 'checks the accessibility of the subject directly' do
+ expect_access_check_on_result
+
+ finder.find_by!(id: result.id)
+ end
+
+ it 're-enables the check after the find failed' do
+ finder.find_by!(id: 9999) rescue ActiveRecord::RecordNotFound
+
+ expect(finder.instance_variable_get(:@should_skip_cross_project_check))
+ .to eq(false)
+ end
+ end
+ end
+
+ context 'when the user can read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project)
+ .and_return(true)
+ end
+
+ it 'returns the result' do
+ expect(finder).not_to receive(:requires_access?)
+
+ expect(finder.execute).to include(result)
+ end
+ end
+end
diff --git a/spec/finders/events_finder_spec.rb b/spec/finders/events_finder_spec.rb
index 18d6c0cfd74..62968e83292 100644
--- a/spec/finders/events_finder_spec.rb
+++ b/spec/finders/events_finder_spec.rb
@@ -26,6 +26,14 @@ describe EventsFinder do
expect(events).not_to include(opened_merge_request_event)
end
+
+ it 'returns nothing when the current user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ events = described_class.new(source: user, current_user: user).execute
+
+ expect(events).to be_empty
+ end
end
context 'when targeting a project' do
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index 06031aee217..dc76efea35b 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -5,6 +5,8 @@ describe LabelsFinder do
let(:group_1) { create(:group) }
let(:group_2) { create(:group) }
let(:group_3) { create(:group) }
+ let(:private_group_1) { create(:group, :private) }
+ let(:private_subgroup_1) { create(:group, :private, parent: private_group_1) }
let(:project_1) { create(:project, namespace: group_1) }
let(:project_2) { create(:project, namespace: group_2) }
@@ -20,6 +22,8 @@ describe LabelsFinder do
let!(:group_label_1) { create(:group_label, group: group_1, title: 'Label 1 (group)') }
let!(:group_label_2) { create(:group_label, group: group_1, title: 'Group Label 2') }
let!(:group_label_3) { create(:group_label, group: group_2, title: 'Group Label 3') }
+ let!(:private_group_label_1) { create(:group_label, group: private_group_1, title: 'Private Group Label 1') }
+ let!(:private_subgroup_label_1) { create(:group_label, group: private_subgroup_1, title: 'Private Sub Group Label 1') }
let(:user) { create(:user) }
@@ -66,6 +70,25 @@ describe LabelsFinder do
expect(finder.execute).to eq [group_label_2, group_label_1]
end
end
+
+ context 'when including labels from group ancestors', :nested_groups do
+ it 'returns labels from group and its ancestors' do
+ private_group_1.add_developer(user)
+ private_subgroup_1.add_developer(user)
+
+ finder = described_class.new(user, group_id: private_subgroup_1.id, only_group_labels: true, include_ancestor_groups: true)
+
+ expect(finder.execute).to eq [private_group_label_1, private_subgroup_label_1]
+ end
+
+ it 'ignores labels from groups which user can not read' do
+ private_subgroup_1.add_developer(user)
+
+ finder = described_class.new(user, group_id: private_subgroup_1.id, only_group_labels: true, include_ancestor_groups: true)
+
+ expect(finder.execute).to eq [private_subgroup_label_1]
+ end
+ end
end
context 'filtering by project_id' do
diff --git a/spec/finders/milestones_finder_spec.rb b/spec/finders/milestones_finder_spec.rb
index 0b3cf7ece5f..656d120311a 100644
--- a/spec/finders/milestones_finder_spec.rb
+++ b/spec/finders/milestones_finder_spec.rb
@@ -70,4 +70,12 @@ describe MilestonesFinder do
expect(result.to_a).to contain_exactly(milestone_1)
end
end
+
+ describe '#find_by' do
+ it 'finds a single milestone' do
+ finder = described_class.new(project_ids: [project_1.id], state: 'all')
+
+ expect(finder.find_by(iid: milestone_3.iid)).to eq(milestone_3)
+ end
+ end
end
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 0a018d2b417..1ae0bd988f2 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -1,57 +1,8 @@
require 'spec_helper'
describe SnippetsFinder do
- let(:user) { create :user }
- let(:user1) { create :user }
- let(:group) { create :group, :public }
-
- let(:project1) { create(:project, :public, group: group) }
- let(:project2) { create(:project, :private, group: group) }
-
- context 'all snippets visible to a user' do
- let!(:snippet1) { create(:personal_snippet, :private) }
- let!(:snippet2) { create(:personal_snippet, :internal) }
- let!(:snippet3) { create(:personal_snippet, :public) }
- let!(:project_snippet1) { create(:project_snippet, :private) }
- let!(:project_snippet2) { create(:project_snippet, :internal) }
- let!(:project_snippet3) { create(:project_snippet, :public) }
-
- it "returns all private and internal snippets" do
- snippets = described_class.new(user, scope: :all).execute
- expect(snippets).to include(snippet2, snippet3, project_snippet2, project_snippet3)
- expect(snippets).not_to include(snippet1, project_snippet1)
- end
-
- it "returns all public snippets" do
- snippets = described_class.new(nil, scope: :all).execute
- expect(snippets).to include(snippet3, project_snippet3)
- expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2)
- end
-
- it "returns all public and internal snippets for normal user" do
- snippets = described_class.new(user).execute
-
- expect(snippets).to include(snippet2, snippet3, project_snippet2, project_snippet3)
- expect(snippets).not_to include(snippet1, project_snippet1)
- end
-
- it "returns all public snippets for non authorized user" do
- snippets = described_class.new(nil).execute
-
- expect(snippets).to include(snippet3, project_snippet3)
- expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2)
- end
-
- it "returns all public and authored snippets for external user" do
- external_user = create(:user, :external)
- authored_snippet = create(:personal_snippet, :internal, author: external_user)
-
- snippets = described_class.new(external_user).execute
-
- expect(snippets).to include(snippet3, project_snippet3, authored_snippet)
- expect(snippets).not_to include(snippet1, snippet2, project_snippet1, project_snippet2)
- end
- end
+ include Gitlab::Allowable
+ using RSpec::Parameterized::TableSyntax
context 'filter by visibility' do
let!(:snippet1) { create(:personal_snippet, :private) }
@@ -67,6 +18,7 @@ describe SnippetsFinder do
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) }
@@ -84,7 +36,7 @@ describe SnippetsFinder do
expect(snippets).not_to include(snippet2, snippet3)
end
- it "returns all snippets for 'are_interna;' scope" do
+ it "returns all snippets for 'are_internal' scope" do
snippets = described_class.new(user, scope: :are_internal).execute
expect(snippets).to include(snippet2)
@@ -100,6 +52,8 @@ describe SnippetsFinder do
end
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) }
@@ -147,6 +101,10 @@ describe SnippetsFinder do
end
context 'filter by project' do
+ let(:user) { create :user }
+ let(:group) { create :group, :public }
+ let(:project1) { create(:project, :public, group: group) }
+
before do
@snippet1 = create(:project_snippet, :private, project: project1)
@snippet2 = create(:project_snippet, :internal, project: project1)
@@ -203,4 +161,27 @@ describe SnippetsFinder do
expect(snippets).to include(@snippet1)
end
end
+
+ 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)
+ end
+
+ context 'when the user cannot read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ it 'returns only personal snippets when the user cannot read cross project' do
+ expect(finder.execute).to contain_exactly(personal_snippet)
+ end
+ end
+ end
end
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
new file mode 100644
index 00000000000..3ca0f7c3c89
--- /dev/null
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe UserRecentEventsFinder do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:project_owner) { project.creator }
+ let!(:event) { create(:event, project: project, author: project_owner) }
+
+ subject(:finder) { described_class.new(user, project_owner) }
+
+ describe '#execute' do
+ it 'does not include the event when a user does not have access to the project' do
+ expect(finder.execute).to be_empty
+ end
+
+ context 'when the user has access to a project' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'includes the event' do
+ expect(finder.execute).to include(event)
+ end
+
+ it 'does not include the event if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ expect(finder.execute).to be_empty
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/api/schemas/deployment.json b/spec/fixtures/api/schemas/deployment.json
new file mode 100644
index 00000000000..536e6475c23
--- /dev/null
+++ b/spec/fixtures/api/schemas/deployment.json
@@ -0,0 +1,45 @@
+{
+ "additionalProperties": false,
+ "properties": {
+ "created_at": {
+ "type": "string"
+ },
+ "id": {
+ "type": "integer"
+ },
+ "iid": {
+ "type": "integer"
+ },
+ "last?": {
+ "type": "boolean"
+ },
+ "ref": {
+ "additionalProperties": false,
+ "properties": {
+ "name": {
+ "type": "string"
+ }
+ },
+ "required": [
+ "name"
+ ],
+ "type": "object"
+ },
+ "sha": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "boolean"
+ }
+ },
+ "required": [
+ "sha",
+ "created_at",
+ "iid",
+ "tag",
+ "last?",
+ "ref",
+ "id"
+ ],
+ "type": "object"
+}
diff --git a/spec/fixtures/api/schemas/deployments.json b/spec/fixtures/api/schemas/deployments.json
index 1112f23aab2..7bf50e4f859 100644
--- a/spec/fixtures/api/schemas/deployments.json
+++ b/spec/fixtures/api/schemas/deployments.json
@@ -3,49 +3,7 @@
"properties": {
"deployments": {
"items": {
- "additionalProperties": false,
- "properties": {
- "created_at": {
- "type": "string"
- },
- "id": {
- "type": "integer"
- },
- "iid": {
- "type": "integer"
- },
- "last?": {
- "type": "boolean"
- },
- "ref": {
- "additionalProperties": false,
- "properties": {
- "name": {
- "type": "string"
- }
- },
- "required": [
- "name"
- ],
- "type": "object"
- },
- "sha": {
- "type": "string"
- },
- "tag": {
- "type": "boolean"
- }
- },
- "required": [
- "sha",
- "created_at",
- "iid",
- "tag",
- "last?",
- "ref",
- "id"
- ],
- "type": "object"
+ "$ref": "deployment.json"
},
"minItems": 1,
"type": "array"
diff --git a/spec/fixtures/api/schemas/public_api/v4/blobs.json b/spec/fixtures/api/schemas/public_api/v4/blobs.json
index 9cb1eae3762..a812815838f 100644
--- a/spec/fixtures/api/schemas/public_api/v4/blobs.json
+++ b/spec/fixtures/api/schemas/public_api/v4/blobs.json
@@ -7,11 +7,12 @@
"data": { "type": "string" },
"filename": { "type": ["string"] },
"id": { "type": ["string", "null"] },
+ "project_id": { "type": "integer" },
"ref": { "type": "string" },
"startline": { "type": "integer" }
},
"required": [
- "basename", "data", "filename", "id", "ref", "startline"
+ "basename", "data", "filename", "id", "ref", "startline", "project_id"
],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/commit/detail.json b/spec/fixtures/api/schemas/public_api/v4/commit/detail.json
index 88a3cad62f6..477e776a804 100644
--- a/spec/fixtures/api/schemas/public_api/v4/commit/detail.json
+++ b/spec/fixtures/api/schemas/public_api/v4/commit/detail.json
@@ -4,9 +4,9 @@
{ "$ref": "basic.json" },
{
"required" : [
- "stats",
"status",
- "last_pipeline"
+ "last_pipeline",
+ "project_id"
],
"properties": {
"stats": { "$ref": "../commit_stats.json" },
@@ -16,7 +16,8 @@
{ "type": "null" },
{ "$ref": "../pipeline/basic.json" }
]
- }
+ },
+ "project_id": { "type": "integer" }
}
}
]
diff --git a/spec/fixtures/api/schemas/public_api/v4/commits_details.json b/spec/fixtures/api/schemas/public_api/v4/commits_details.json
new file mode 100644
index 00000000000..1f5b1ad86ef
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/commits_details.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "commit/detail.json" }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
index e8c17298b43..ed8ed9085c0 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/basic.json
@@ -4,6 +4,9 @@
"domain": { "type": "string" },
"url": { "type": "uri" },
"project_id": { "type": "integer" },
+ "verified": { "type": "boolean" },
+ "verification_code": { "type": ["string", "null"] },
+ "enabled_until": { "type": ["date", "null"] },
"certificate_expiration": {
"type": "object",
"properties": {
@@ -14,6 +17,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url", "project_id"],
+ "required": ["domain", "url", "project_id", "verified", "verification_code", "enabled_until"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
index 08db8d47050..b57d544f896 100644
--- a/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
+++ b/spec/fixtures/api/schemas/public_api/v4/pages_domain/detail.json
@@ -3,6 +3,9 @@
"properties": {
"domain": { "type": "string" },
"url": { "type": "uri" },
+ "verified": { "type": "boolean" },
+ "verification_code": { "type": ["string", "null"] },
+ "enabled_until": { "type": ["date", "null"] },
"certificate": {
"type": "object",
"properties": {
@@ -15,6 +18,6 @@
"additionalProperties": false
}
},
- "required": ["domain", "url"],
+ "required": ["domain", "url", "verified", "verification_code", "enabled_until"],
"additionalProperties": false
}
diff --git a/spec/fixtures/api/schemas/variable.json b/spec/fixtures/api/schemas/variable.json
index 78977118b0a..6f6b044115b 100644
--- a/spec/fixtures/api/schemas/variable.json
+++ b/spec/fixtures/api/schemas/variable.json
@@ -10,7 +10,8 @@
"id": { "type": "integer" },
"key": { "type": "string" },
"value": { "type": "string" },
- "protected": { "type": "boolean" }
+ "protected": { "type": "boolean" },
+ "environment_scope": { "type": "string", "optional": true }
},
"additionalProperties": false
}
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index f7a4a7afced..43cb0dfe163 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -63,13 +63,29 @@ describe ApplicationHelper do
end
end
- describe 'avatar_icon' do
+ describe 'avatar_icon_for' do
+ let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
+ let(:email) { 'foo@example.com' }
+ let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
+
+ it 'prefers the user to retrieve the avatar_url' do
+ expect(helper.avatar_icon_for(user, email).to_s)
+ .to eq(user.avatar.url)
+ end
+
+ it 'falls back to email lookup if no user given' do
+ expect(helper.avatar_icon_for(nil, email).to_s)
+ .to eq(another_user.avatar.url)
+ end
+ end
+
+ describe 'avatar_icon_for_email' do
let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
context 'using an email' do
context 'when there is a matching user' do
it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon(user.email).to_s)
+ expect(helper.avatar_icon_for_email(user.email).to_s)
.to eq(user.avatar.url)
end
end
@@ -78,17 +94,37 @@ describe ApplicationHelper do
it 'calls gravatar_icon' do
expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
- helper.avatar_icon('foo@example.com', 20, 2)
+ helper.avatar_icon_for_email('foo@example.com', 20, 2)
+ end
+ end
+
+ context 'without an email passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_email(nil, 20, 2)
end
end
end
+ end
+
+ describe 'avatar_icon_for_user' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
- describe 'using a user' do
+ context 'with a user object passed' do
it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon(user).to_s)
+ expect(helper.avatar_icon_for_user(user).to_s)
.to eq(user.avatar.url)
end
end
+
+ context 'without a user object passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_user(nil, 20, 2)
+ end
+ end
end
describe 'gravatar_icon' do
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index f44e7ef6843..04c6d259135 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -29,7 +29,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
- src: avatar_icon(user, 16),
+ src: avatar_icon_for_user(user, 16),
data: { container: 'body' },
class: 'avatar s16 has-tooltip',
title: user.name
@@ -43,7 +43,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
- src: avatar_icon(user, 16),
+ src: avatar_icon_for_user(user, 16),
data: { container: 'body' },
class: "avatar s16 #{options[:css_class]} has-tooltip",
title: user.name
@@ -58,7 +58,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
- src: avatar_icon(user, options[:size]),
+ src: avatar_icon_for_user(user, options[:size]),
data: { container: 'body' },
class: "avatar s#{options[:size]} has-tooltip",
title: user.name
@@ -89,7 +89,7 @@ describe AvatarsHelper do
:img,
alt: "#{user.name}'s avatar",
src: LazyImageTagHelper.placeholder_image,
- data: { container: 'body', src: avatar_icon(user, 16) },
+ data: { container: 'body', src: avatar_icon_for_user(user, 16) },
class: "avatar s16 has-tooltip lazy",
title: user.name
)
@@ -104,7 +104,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
- src: avatar_icon(user, 16),
+ src: avatar_icon_for_user(user, 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: user.name
@@ -119,7 +119,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
- src: avatar_icon(user, 16),
+ src: avatar_icon_for_user(user, 16),
class: "avatar s16",
title: user.name
)
@@ -137,7 +137,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{user.name}'s avatar",
- src: avatar_icon(user, 16),
+ src: avatar_icon_for_user(user, 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: user.name
@@ -149,7 +149,7 @@ describe AvatarsHelper do
is_expected.to eq tag(
:img,
alt: "#{options[:user_name]}'s avatar",
- src: avatar_icon(options[:user_email], 16),
+ src: avatar_icon_for_email(options[:user_email], 16),
data: { container: 'body' },
class: "avatar s16 has-tooltip",
title: options[:user_name]
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index a030796c54e..1fa194fe1b8 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -86,7 +86,7 @@ describe BlobHelper do
it 'verifies blob is text' do
expect(helper).not_to receive(:blob_text_viewable?)
- button = edit_blob_link(project, 'refs/heads/master', 'README.md')
+ button = edit_blob_button(project, 'refs/heads/master', 'README.md')
expect(button).to start_with('<button')
end
@@ -96,17 +96,17 @@ describe BlobHelper do
expect(project.repository).not_to receive(:blob_at)
- edit_blob_link(project, 'refs/heads/master', 'README.md', blob: blob)
+ edit_blob_button(project, 'refs/heads/master', 'README.md', blob: blob)
end
it 'returns a link with the proper route' do
- link = edit_blob_link(project, 'master', 'README.md')
+ 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 the passed link_opts on the expected route' do
- link = edit_blob_link(project, 'master', 'README.md', link_opts: { mr_id: 10 })
+ 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")
end
diff --git a/spec/helpers/dashboard_helper_spec.rb b/spec/helpers/dashboard_helper_spec.rb
new file mode 100644
index 00000000000..7ba24ba2956
--- /dev/null
+++ b/spec/helpers/dashboard_helper_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe DashboardHelper do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?) { true }
+ end
+
+ describe '#dashboard_nav_links' do
+ it 'has all the expected links by default' do
+ menu_items = [:projects, :groups, :activity, :milestones, :snippets]
+
+ expect(helper.dashboard_nav_links).to contain_exactly(*menu_items)
+ end
+
+ it 'does not contain cross project elements when the user cannot read cross project' do
+ expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
+
+ expect(helper.dashboard_nav_links).not_to include(:activity, :milestones)
+ end
+ end
+end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index 8a80b88da5d..fccde8b7eba 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -20,5 +20,9 @@ describe EventsHelper do
it 'handles nil values' do
expect(helper.event_commit_title(nil)).to eq('')
end
+
+ it 'does not escape HTML entities' do
+ expect(helper.event_commit_title("foo & bar")).to eq("foo & bar")
+ end
end
end
diff --git a/spec/helpers/explore_helper_spec.rb b/spec/helpers/explore_helper_spec.rb
new file mode 100644
index 00000000000..12651d80e36
--- /dev/null
+++ b/spec/helpers/explore_helper_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe ExploreHelper do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?) { true }
+ end
+
+ describe '#explore_nav_links' do
+ it 'has all the expected links by default' do
+ menu_items = [:projects, :groups, :snippets]
+
+ expect(helper.explore_nav_links).to contain_exactly(*menu_items)
+ end
+ end
+end
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 5f608fe18d9..b48c252acd3 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -201,4 +201,39 @@ describe GroupsHelper do
end
end
end
+
+ describe '#group_sidebar_links' do
+ let(:group) { create(:group, :public) }
+ let(:user) { create(:user) }
+ before do
+ allow(helper).to receive(:current_user) { user }
+ allow(helper).to receive(:can?) { true }
+ helper.instance_variable_set(:@group, group)
+ end
+
+ it 'returns all the expected links' do
+ links = [
+ :overview, :activity, :issues, :labels, :milestones, :merge_requests,
+ :group_members, :settings
+ ]
+
+ expect(helper.group_sidebar_links).to include(*links)
+ end
+
+ it 'includes settings when the user can admin the group' do
+ expect(helper).to receive(:current_user) { user }
+ expect(helper).to receive(:can?).with(user, :admin_group, group) { false }
+
+ expect(helper.group_sidebar_links).not_to include(:settings)
+ end
+
+ it 'excludes cross project features when the user cannot read cross project' do
+ cross_project_features = [:activity, :issues, :labels, :milestones,
+ :merge_requests]
+
+ expect(helper).to receive(:can?).with(user, :read_cross_project) { false }
+
+ expect(helper.group_sidebar_links).not_to include(*cross_project_features)
+ end
+ end
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index 7fa665aecdc..2fecd1a3d27 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -173,23 +173,23 @@ describe IssuablesHelper do
@project = issue.project
expected_data = {
- 'endpoint' => "/#{@project.full_path}/issues/#{issue.iid}",
- 'updateEndpoint' => "/#{@project.full_path}/issues/#{issue.iid}.json",
- 'canUpdate' => true,
- 'canDestroy' => true,
- 'issuableRef' => "##{issue.iid}",
- 'markdownPreviewPath' => "/#{@project.full_path}/preview_markdown",
- 'markdownDocsPath' => '/help/user/markdown',
- 'issuableTemplates' => [],
- 'projectPath' => @project.path,
- 'projectNamespace' => @project.namespace.path,
- 'initialTitleHtml' => issue.title,
- 'initialTitleText' => issue.title,
- 'initialDescriptionHtml' => '<p dir="auto">issue text</p>',
- 'initialDescriptionText' => 'issue text',
- 'initialTaskStatus' => '0 of 0 tasks completed'
+ endpoint: "/#{@project.full_path}/issues/#{issue.iid}",
+ updateEndpoint: "/#{@project.full_path}/issues/#{issue.iid}.json",
+ canUpdate: true,
+ canDestroy: true,
+ issuableRef: "##{issue.iid}",
+ markdownPreviewPath: "/#{@project.full_path}/preview_markdown",
+ markdownDocsPath: '/help/user/markdown',
+ issuableTemplates: [],
+ projectPath: @project.path,
+ projectNamespace: @project.namespace.path,
+ initialTitleHtml: issue.title,
+ initialTitleText: issue.title,
+ initialDescriptionHtml: '<p dir="auto">issue text</p>',
+ initialDescriptionText: 'issue text',
+ initialTaskStatus: '0 of 0 tasks completed'
}
- expect(JSON.parse(helper.issuable_initial_data(issue))).to eq(expected_data)
+ expect(helper.issuable_initial_data(issue)).to eq(expected_data)
end
end
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index ddf881a7b6f..aeef5352333 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -113,21 +113,6 @@ describe IssuesHelper do
end
end
- describe "milestone_options" do
- it "gets closed milestone from current issue" do
- closed_milestone = create(:closed_milestone, project: project)
- milestone1 = create(:milestone, project: project)
- milestone2 = create(:milestone, project: project)
- issue.update_attributes(milestone_id: closed_milestone.id)
-
- options = milestone_options(issue)
-
- expect(options).to have_selector('option[selected]', text: closed_milestone.title)
- expect(options).to have_selector('option', text: milestone1.title)
- expect(options).to have_selector('option', text: milestone2.title)
- end
- end
-
describe "#link_to_discussions_to_resolve" do
describe "passing only a merge request" do
let(:merge_request) { create(:merge_request) }
diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb
new file mode 100644
index 00000000000..e840c927d59
--- /dev/null
+++ b/spec/helpers/nav_helper_spec.rb
@@ -0,0 +1,53 @@
+require 'spec_helper'
+
+describe NavHelper do
+ describe '#header_links' do
+ before do
+ allow(helper).to receive(:session) { {} }
+ end
+
+ context 'when the user is logged in' do
+ let(:user) { build(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?) { true }
+ end
+
+ it 'has all the expected links by default' do
+ menu_items = [:user_dropdown, :search, :issues, :merge_requests, :todos]
+
+ expect(helper.header_links).to contain_exactly(*menu_items)
+ end
+
+ it 'contains the impersonation link while impersonating' do
+ expect(helper).to receive(:session) { { impersonator_id: 1 } }
+
+ expect(helper.header_links).to include(:admin_impersonation)
+ end
+
+ context 'when the user cannot read cross project' do
+ before do
+ allow(helper).to receive(:can?).with(user, :read_cross_project) { false }
+ end
+
+ it 'does not contain cross project elements when the user cannot read cross project' do
+ expect(helper.header_links).not_to include(:issues, :merge_requests, :todos, :search)
+ end
+
+ it 'shows the search box when the user cannot read cross project and he is visiting a project' do
+ helper.instance_variable_set(:@project, create(:project))
+
+ expect(helper.header_links).to include(:search)
+ end
+ end
+ end
+
+ it 'returns only the sign in and search when the user is not logged in' do
+ allow(helper).to receive(:current_user).and_return(nil)
+ allow(helper).to receive(:can?).with(nil, :read_cross_project) { true }
+
+ expect(helper.header_links).to contain_exactly(:sign_in, :search)
+ end
+ end
+end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 749aa25e632..e2a0c4322ff 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -77,103 +77,6 @@ describe PreferencesHelper do
end
end
- describe '#default_project_view' do
- context 'user not signed in' do
- before do
- helper.instance_variable_set(:@project, project)
- stub_user
- end
-
- context 'when repository is empty' do
- let(:project) { create(:project_empty_repo, :public) }
-
- it 'returns activity if user has repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true)
-
- expect(helper.default_project_view).to eq('activity')
- end
-
- it 'returns activity if user does not have repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(false)
-
- expect(helper.default_project_view).to eq('activity')
- end
- end
-
- context 'when repository is not empty' do
- let(:project) { create(:project, :public, :repository) }
-
- it 'returns files and readme if user has repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(true)
-
- expect(helper.default_project_view).to eq('files')
- end
-
- it 'returns activity if user does not have repository access' do
- allow(helper).to receive(:can?).with(nil, :download_code, project).and_return(false)
-
- expect(helper.default_project_view).to eq('activity')
- end
- end
- end
-
- context 'user signed in' do
- let(:user) { create(:user, :readme) }
- let(:project) { create(:project, :public, :repository) }
-
- before do
- helper.instance_variable_set(:@project, project)
- allow(helper).to receive(:current_user).and_return(user)
- end
-
- context 'when the user is allowed to see the code' do
- it 'returns the project view' do
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(true)
-
- expect(helper.default_project_view).to eq('readme')
- end
- end
-
- context 'with wikis enabled and the right policy for the user' do
- before do
- project.project_feature.update_attribute(:issues_access_level, 0)
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
- end
-
- it 'returns wiki if the user has the right policy' do
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(true)
-
- expect(helper.default_project_view).to eq('wiki')
- end
-
- it 'returns customize_workflow if the user does not have the right policy' do
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
-
- expect(helper.default_project_view).to eq('customize_workflow')
- end
- end
-
- context 'with issues as a feature available' do
- it 'return issues' do
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
-
- expect(helper.default_project_view).to eq('projects/issues/issues')
- end
- end
-
- context 'with no activity, no wikies and no issues' do
- it 'returns customize_workflow as default' do
- project.project_feature.update_attribute(:issues_access_level, 0)
- allow(helper).to receive(:can?).with(user, :download_code, project).and_return(false)
- allow(helper).to receive(:can?).with(user, :read_wiki, project).and_return(false)
-
- expect(helper.default_project_view).to eq('customize_workflow')
- end
- end
- 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 c0251bf7dc0..ce96e90e2d7 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -75,6 +75,12 @@ describe ProjectsHelper do
describe "#project_list_cache_key", :clean_gitlab_redis_shared_state do
let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).with(user, :read_cross_project) { true }
+ end
it "includes the route" do
expect(helper.project_list_cache_key(project)).to include(project.route.cache_key)
@@ -106,6 +112,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project).last).to start_with('v')
end
+ it 'includes wether or not the user can read cross project' do
+ expect(helper.project_list_cache_key(project)).to include('cross-project:true')
+ end
+
it "includes the pipeline status when there is a status" do
create(:ci_pipeline, :success, project: project, sha: project.commit.sha)
@@ -215,7 +225,7 @@ describe ProjectsHelper do
let(:expected) { double }
before do
- expect(helper).to receive(:avatar_icon).with(user, 16).and_return(expected)
+ expect(helper).to receive(:avatar_icon_for_user).with(user, 16).and_return(expected)
end
it 'returns image tag for member avatar' do
@@ -264,32 +274,6 @@ describe ProjectsHelper do
end
end
- describe '#license_short_name' do
- let(:project) { create(:project) }
-
- context 'when project.repository has a license_key' do
- it 'returns the nickname of the license if present' do
- allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
-
- expect(helper.license_short_name(project)).to eq('GNU AGPLv3')
- end
-
- it 'returns the name of the license if nickname is not present' do
- allow(project.repository).to receive(:license_key).and_return('mit')
-
- expect(helper.license_short_name(project)).to eq('MIT License')
- end
- end
-
- context 'when project.repository has no license_key but a license_blob' do
- it 'returns LICENSE' do
- allow(project.repository).to receive(:license_key).and_return(nil)
-
- expect(helper.license_short_name(project)).to eq('LICENSE')
- end
- end
- end
-
describe '#sanitized_import_error' do
let(:project) { create(:project, :repository) }
@@ -462,6 +446,22 @@ describe ProjectsHelper do
end
end
+ describe('#push_to_create_project_command') do
+ let(:user) { create(:user, username: 'john') }
+
+ it 'returns the command to push to create project over HTTP' do
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'http' }
+
+ expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream http://test.host/john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
+ end
+
+ it 'returns the command to push to create project over SSH' do
+ allow(Gitlab::CurrentSettings.current_application_settings).to receive(:enabled_git_access_protocol) { 'ssh' }
+
+ expect(helper.push_to_create_project_command(user)).to eq('git push --set-upstream git@localhost:john/$(git rev-parse --show-toplevel | xargs basename).git $(git rev-parse --abbrev-ref HEAD)')
+ end
+ end
+
describe '#any_projects?' do
let!(:project) { create(:project) }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
index 03f78de8e91..6332217b920 100644
--- a/spec/helpers/users_helper_spec.rb
+++ b/spec/helpers/users_helper_spec.rb
@@ -14,4 +14,17 @@ describe UsersHelper do
is_expected.to include("title=\"#{user.email}\"")
end
end
+
+ describe '#profile_tabs' do
+ subject(:tabs) { helper.profile_tabs }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(true)
+ end
+
+ it 'includes all the expected tabs' do
+ expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets)
+ end
+ end
end
diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js
index c93b7cc6cac..95c2c122403 100644
--- a/spec/javascripts/ajax_loading_spinner_spec.js
+++ b/spec/javascripts/ajax_loading_spinner_spec.js
@@ -1,5 +1,3 @@
-import 'jquery';
-import 'jquery-ujs';
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
describe('Ajax Loading Spinner', () => {
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index 268b5b83b73..8e4bbb90ccb 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -79,7 +79,7 @@ import '~/lib/utils/common_utils';
return expect($emojiMenu.length).toBe(1);
});
});
- return it('should remove emoji menu when body is clicked', function(done) {
+ it('should remove emoji menu when body is clicked', function(done) {
$('.js-add-award').eq(0).click();
return lazyAssert(done, function() {
var $emojiMenu;
@@ -90,6 +90,17 @@ import '~/lib/utils/common_utils';
return expect($('.js-awards-block.current').length).toBe(0);
});
});
+ it('should not remove emoji menu when search is clicked', function(done) {
+ $('.js-add-award').eq(0).click();
+ return lazyAssert(done, function() {
+ var $emojiMenu;
+ $emojiMenu = $('.emoji-menu');
+ $('.emoji-search').click();
+ expect($emojiMenu.length).toBe(1);
+ expect($emojiMenu.hasClass('is-visible')).toBe(true);
+ return expect($('.js-awards-block.current').length).toBe(1);
+ });
+ });
});
describe('::addAwardToEmojiBar', function() {
it('should add emoji to votes block', function() {
@@ -127,7 +138,7 @@ import '~/lib/utils/common_utils';
$thumbsUpEmoji = $votesBlock.find('[data-name=thumbsup]').parent();
$thumbsUpEmoji.attr('data-title', 'sam');
awardsHandler.userAuthored($thumbsUpEmoji);
- return expect($thumbsUpEmoji.data("original-title")).toBe("You cannot vote on your own issue, MR and note");
+ return expect($thumbsUpEmoji.data("originalTitle")).toBe("You cannot vote on your own issue, MR and note");
});
it('should restore tooltip back to initial vote list', function() {
var $thumbsUpEmoji, $votesBlock;
@@ -138,7 +149,7 @@ import '~/lib/utils/common_utils';
awardsHandler.userAuthored($thumbsUpEmoji);
jasmine.clock().tick(2801);
jasmine.clock().uninstall();
- return expect($thumbsUpEmoji.data("original-title")).toBe("sam");
+ return expect($thumbsUpEmoji.data("originalTitle")).toBe("sam");
});
});
describe('::getAwardUrl', function() {
@@ -183,7 +194,7 @@ import '~/lib/utils/common_utils';
$thumbsUpEmoji.attr('data-title', 'sam, jerry, max, and andy');
awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
$thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data("original-title")).toBe('You, sam, jerry, max, and andy');
+ return expect($thumbsUpEmoji.data("originalTitle")).toBe('You, sam, jerry, max, and andy');
});
return it('handles the special case where "You" is not cleanly comma seperated', function() {
var $thumbsUpEmoji, $votesBlock, awardUrl;
@@ -193,7 +204,7 @@ import '~/lib/utils/common_utils';
$thumbsUpEmoji.attr('data-title', 'sam');
awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
$thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data("original-title")).toBe('You and sam');
+ return expect($thumbsUpEmoji.data("originalTitle")).toBe('You and sam');
});
});
describe('::removeYouToUserList', function() {
@@ -206,7 +217,7 @@ import '~/lib/utils/common_utils';
$thumbsUpEmoji.addClass('active');
awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
$thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data("original-title")).toBe('sam, jerry, max, and andy');
+ return expect($thumbsUpEmoji.data("originalTitle")).toBe('sam, jerry, max, and andy');
});
return it('handles the special case where "You" is not cleanly comma seperated', function() {
var $thumbsUpEmoji, $votesBlock, awardUrl;
@@ -217,7 +228,7 @@ import '~/lib/utils/common_utils';
$thumbsUpEmoji.addClass('active');
awardsHandler.addAward($votesBlock, awardUrl, 'thumbsup', false);
$thumbsUpEmoji.tooltip();
- return expect($thumbsUpEmoji.data("original-title")).toBe('sam');
+ return expect($thumbsUpEmoji.data("originalTitle")).toBe('sam');
});
});
describe('::searchEmojis', () => {
diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js
index 8287c58ac5a..e500bbe750f 100644
--- a/spec/javascripts/behaviors/requires_input_spec.js
+++ b/spec/javascripts/behaviors/requires_input_spec.js
@@ -15,7 +15,7 @@ describe('requiresInput', () => {
});
it('enables submit when no field is required', () => {
- $('*[required=required]').removeAttr('required');
+ $('*[required=required]').prop('required', false);
$('.js-requires-input').requiresInput();
expect(submitButton).not.toBeDisabled();
});
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index e204985f039..d5fbfdeaa91 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -4,7 +4,7 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import boardNewIssue from '~/boards/components/board_new_issue';
+import boardNewIssue from '~/boards/components/board_new_issue.vue';
import '~/boards/models/list';
import { listObj, boardsMockInterceptor, mockBoardService } from './mock_data';
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 5b9cdceee71..ee457a9c48c 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -1,8 +1,10 @@
+import $ from 'jquery';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import AjaxFormVariableList from '~/ci_variable_list/ajax_variable_list';
const VARIABLE_PATCH_ENDPOINT = 'http://test.host/frontend-fixtures/builds-project/variables';
+const HIDE_CLASS = 'hide';
describe('AjaxFormVariableList', () => {
preloadFixtures('projects/ci_cd_settings.html.raw');
@@ -45,16 +47,16 @@ describe('AjaxFormVariableList', () => {
const loadingIcon = saveButton.querySelector('.js-secret-variables-save-loading-icon');
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(() => {
- expect(loadingIcon.classList.contains('hide')).toEqual(false);
+ expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(false);
return [200, {}];
});
- expect(loadingIcon.classList.contains('hide')).toEqual(true);
+ expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true);
ajaxVariableList.onSaveClicked()
.then(() => {
- expect(loadingIcon.classList.contains('hide')).toEqual(true);
+ expect(loadingIcon.classList.contains(HIDE_CLASS)).toEqual(true);
})
.then(done)
.catch(done.fail);
@@ -78,11 +80,11 @@ describe('AjaxFormVariableList', () => {
it('hides any previous error box', (done) => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200);
- expect(errorBox.classList.contains('hide')).toEqual(true);
+ expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
ajaxVariableList.onSaveClicked()
.then(() => {
- expect(errorBox.classList.contains('hide')).toEqual(true);
+ expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
})
.then(done)
.catch(done.fail);
@@ -103,17 +105,39 @@ describe('AjaxFormVariableList', () => {
.catch(done.fail);
});
+ it('hides secret values', (done) => {
+ mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(200, {});
+
+ const row = container.querySelector('.js-row:first-child');
+ const valueInput = row.querySelector('.js-ci-variable-input-value');
+ const valuePlaceholder = row.querySelector('.js-secret-value-placeholder');
+
+ valueInput.value = 'bar';
+ $(valueInput).trigger('input');
+
+ expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(true);
+ expect(valueInput.classList.contains(HIDE_CLASS)).toBe(false);
+
+ ajaxVariableList.onSaveClicked()
+ .then(() => {
+ expect(valuePlaceholder.classList.contains(HIDE_CLASS)).toBe(false);
+ expect(valueInput.classList.contains(HIDE_CLASS)).toBe(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
it('shows error box with validation errors', (done) => {
const validationError = 'some validation error';
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(400, [
validationError,
]);
- expect(errorBox.classList.contains('hide')).toEqual(true);
+ expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
ajaxVariableList.onSaveClicked()
.then(() => {
- expect(errorBox.classList.contains('hide')).toEqual(false);
+ expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(false);
expect(errorBox.textContent.trim().replace(/\n+\s+/m, ' ')).toEqual(`Validation failed ${validationError}`);
})
.then(done)
@@ -123,11 +147,11 @@ describe('AjaxFormVariableList', () => {
it('shows flash message when request fails', (done) => {
mock.onPatch(VARIABLE_PATCH_ENDPOINT).reply(500);
- expect(errorBox.classList.contains('hide')).toEqual(true);
+ expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
ajaxVariableList.onSaveClicked()
.then(() => {
- expect(errorBox.classList.contains('hide')).toEqual(true);
+ expect(errorBox.classList.contains(HIDE_CLASS)).toEqual(true);
})
.then(done)
.catch(done.fail);
@@ -170,9 +194,9 @@ describe('AjaxFormVariableList', () => {
const valueInput = row.querySelector('.js-ci-variable-input-value');
keyInput.value = 'foo';
- keyInput.dispatchEvent(new Event('input'));
+ $(keyInput).trigger('input');
valueInput.value = 'bar';
- valueInput.dispatchEvent(new Event('input'));
+ $(valueInput).trigger('input');
expect(idInput.value).toEqual('');
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 6ab7b50e035..270f925e699 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -1,5 +1,7 @@
import VariableList from '~/ci_variable_list/ci_variable_list';
-import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper';
+import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
+
+const HIDE_CLASS = 'hide';
describe('VariableList', () => {
preloadFixtures('pipeline_schedules/edit.html.raw');
@@ -92,14 +94,14 @@ describe('VariableList', () => {
const $inputValue = $row.find('.js-ci-variable-input-value');
const $placeholder = $row.find('.js-secret-value-placeholder');
- expect($placeholder.hasClass('hide')).toBe(false);
- expect($inputValue.hasClass('hide')).toBe(true);
+ expect($placeholder.hasClass(HIDE_CLASS)).toBe(false);
+ expect($inputValue.hasClass(HIDE_CLASS)).toBe(true);
// Reveal values
$wrapper.find('.js-secret-value-reveal-button').click();
- expect($placeholder.hasClass('hide')).toBe(true);
- expect($inputValue.hasClass('hide')).toBe(false);
+ expect($placeholder.hasClass(HIDE_CLASS)).toBe(true);
+ expect($inputValue.hasClass(HIDE_CLASS)).toBe(false);
});
});
});
@@ -126,7 +128,7 @@ describe('VariableList', () => {
// Check for the correct default in the new row
const $protectedInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-protected');
- expect($protectedInput.val()).toBe('true');
+ expect($protectedInput.val()).toBe('false');
})
.then(done)
.catch(done.fail);
@@ -179,4 +181,35 @@ describe('VariableList', () => {
expect($wrapper.find('.js-ci-variable-input-key:not([disabled])').length).toBe(3);
});
});
+
+ describe('hideValues', () => {
+ beforeEach(() => {
+ loadFixtures('projects/ci_cd_settings.html.raw');
+ $wrapper = $('.js-ci-variable-list-section');
+
+ variableList = new VariableList({
+ container: $wrapper,
+ formField: 'variables',
+ });
+ variableList.init();
+ });
+
+ it('should hide value input and show placeholder stars', () => {
+ const $row = $wrapper.find('.js-row');
+ const $inputValue = $row.find('.js-ci-variable-input-value');
+ const $placeholder = $row.find('.js-secret-value-placeholder');
+
+ $row.find('.js-ci-variable-input-value')
+ .val('foo')
+ .trigger('input');
+
+ expect($placeholder.hasClass(HIDE_CLASS)).toBe(true);
+ expect($inputValue.hasClass(HIDE_CLASS)).toBe(false);
+
+ variableList.hideValues();
+
+ expect($placeholder.hasClass(HIDE_CLASS)).toBe(false);
+ expect($inputValue.hasClass(HIDE_CLASS)).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index a9e244e523d..a5cd247b689 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -7,7 +7,7 @@ import {
REQUEST_SUCCESS,
REQUEST_FAILURE,
} from '~/clusters/constants';
-import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper';
+import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
describe('Clusters', () => {
let cluster;
diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js
index e671c18e1a5..2c4707bb856 100644
--- a/spec/javascripts/clusters/components/application_row_spec.js
+++ b/spec/javascripts/clusters/components/application_row_spec.js
@@ -12,7 +12,7 @@ import {
REQUEST_FAILURE,
} from '~/clusters/constants';
import applicationRow from '~/clusters/components/application_row.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { DEFAULT_APPLICATION_STATE } from '../services/mock_data';
describe('Application Row', () => {
diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js
index 1a8affad4e3..15832c38f25 100644
--- a/spec/javascripts/clusters/components/applications_spec.js
+++ b/spec/javascripts/clusters/components/applications_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import applications from '~/clusters/components/applications.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Applications', () => {
let vm;
diff --git a/spec/javascripts/commit/commit_pipeline_status_component_spec.js b/spec/javascripts/commit/commit_pipeline_status_component_spec.js
index 90f290e845e..421fe62a1e7 100644
--- a/spec/javascripts/commit/commit_pipeline_status_component_spec.js
+++ b/spec/javascripts/commit/commit_pipeline_status_component_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Commit pipeline status component', () => {
let vm;
diff --git a/spec/javascripts/commits_spec.js b/spec/javascripts/commits_spec.js
index 44ec9e4eabf..1daccc8dd02 100644
--- a/spec/javascripts/commits_spec.js
+++ b/spec/javascripts/commits_spec.js
@@ -4,6 +4,8 @@ import axios from '~/lib/utils/axios_utils';
import CommitsList from '~/commits';
describe('Commits List', () => {
+ let commitsList;
+
beforeEach(() => {
setFixtures(`
<form class="commits-search-form" action="/h5bp/html5-boilerplate/commits/master">
@@ -11,6 +13,7 @@ describe('Commits List', () => {
</form>
<ol id="commits-list"></ol>
`);
+ commitsList = new CommitsList(25);
});
it('should be defined', () => {
@@ -19,7 +22,7 @@ describe('Commits List', () => {
describe('processCommits', () => {
it('should join commit headers', () => {
- CommitsList.$contentList = $(`
+ commitsList.$contentList = $(`
<div>
<li class="commit-header" data-day="2016-09-20">
<span class="day">20 Sep, 2016</span>
@@ -39,7 +42,7 @@ describe('Commits List', () => {
// The last commit header should be removed
// since the previous one has the same data-day value.
- expect(CommitsList.processCommits(data).find('li.commit-header').length).toBe(0);
+ expect(commitsList.processCommits(data).find('li.commit-header').length).toBe(0);
});
});
@@ -48,8 +51,7 @@ describe('Commits List', () => {
let mock;
beforeEach(() => {
- CommitsList.init(25);
- CommitsList.searchField.val('');
+ commitsList.searchField.val('');
spyOn(history, 'replaceState').and.stub();
mock = new MockAdapter(axios);
@@ -66,11 +68,11 @@ describe('Commits List', () => {
});
it('should save the last search string', (done) => {
- CommitsList.searchField.val('GitLab');
- CommitsList.filterResults()
+ commitsList.searchField.val('GitLab');
+ commitsList.filterResults()
.then(() => {
expect(ajaxSpy).toHaveBeenCalled();
- expect(CommitsList.lastSearch).toEqual('GitLab');
+ expect(commitsList.lastSearch).toEqual('GitLab');
done();
})
@@ -78,10 +80,10 @@ describe('Commits List', () => {
});
it('should not make ajax call if the input does not change', (done) => {
- CommitsList.filterResults()
+ commitsList.filterResults()
.then(() => {
expect(ajaxSpy).not.toHaveBeenCalled();
- expect(CommitsList.lastSearch).toEqual('');
+ expect(commitsList.lastSearch).toEqual('');
done();
})
diff --git a/spec/javascripts/cycle_analytics/banner_spec.js b/spec/javascripts/cycle_analytics/banner_spec.js
index 64a76a6ee5f..2815bdba0c2 100644
--- a/spec/javascripts/cycle_analytics/banner_spec.js
+++ b/spec/javascripts/cycle_analytics/banner_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import banner from '~/cycle_analytics/components/banner.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Cycle analytics banner', () => {
let vm;
diff --git a/spec/javascripts/cycle_analytics/total_time_component_spec.js b/spec/javascripts/cycle_analytics/total_time_component_spec.js
index ad0fc38a856..691e03cb8a6 100644
--- a/spec/javascripts/cycle_analytics/total_time_component_spec.js
+++ b/spec/javascripts/cycle_analytics/total_time_component_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import component from '~/cycle_analytics/components/total_time_component.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Total time component', () => {
let vm;
diff --git a/spec/javascripts/droplab/drop_down_spec.js b/spec/javascripts/droplab/drop_down_spec.js
index 1225fe2cb66..896a04a1a07 100644
--- a/spec/javascripts/droplab/drop_down_spec.js
+++ b/spec/javascripts/droplab/drop_down_spec.js
@@ -1,8 +1,8 @@
import DropDown from '~/droplab/drop_down';
import utils from '~/droplab/utils';
-import { SELECTED_CLASS, IGNORE_CLASS } from '~/droplab/constants';
+import { SELECTED_CLASS } from '~/droplab/constants';
-describe('DropDown', function () {
+describe('DropLab DropDown', function () {
describe('class constructor', function () {
beforeEach(function () {
spyOn(DropDown.prototype, 'getItems');
@@ -128,93 +128,131 @@ describe('DropDown', function () {
beforeEach(function () {
this.classList = jasmine.createSpyObj('classList', ['contains']);
this.list = { dispatchEvent: () => {} };
- this.dropdown = { hide: () => {}, list: this.list, addSelectedClass: () => {} };
- this.event = { preventDefault: () => {}, target: { classList: this.classList } };
+ this.dropdown = {
+ hideOnClick: true,
+ hide: () => {},
+ list: this.list,
+ addSelectedClass: () => {},
+ };
+ this.event = {
+ preventDefault: () => {},
+ target: {
+ classList: this.classList,
+ closest: () => null,
+ },
+ };
this.customEvent = {};
- this.closestElement = {};
+ this.dummyListItem = document.createElement('li');
+ spyOn(this.event.target, 'closest').and.callFake((selector) => {
+ if (selector === 'li') {
+ return this.dummyListItem;
+ }
+
+ return null;
+ });
spyOn(this.dropdown, 'hide');
spyOn(this.dropdown, 'addSelectedClass');
spyOn(this.list, 'dispatchEvent');
spyOn(this.event, 'preventDefault');
spyOn(window, 'CustomEvent').and.returnValue(this.customEvent);
- spyOn(utils, 'closest').and.returnValues(this.closestElement, undefined);
this.classList.contains.and.returnValue(false);
+ });
+ it('should call event.target.closest', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
- });
- it('should call utils.closest', function () {
- expect(utils.closest).toHaveBeenCalledWith(this.event.target, 'LI');
+ expect(this.event.target.closest).toHaveBeenCalledWith('.droplab-item-ignore');
+ expect(this.event.target.closest).toHaveBeenCalledWith('li');
});
it('should call addSelectedClass', function () {
- expect(this.dropdown.addSelectedClass).toHaveBeenCalledWith(this.closestElement);
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
+ expect(this.dropdown.addSelectedClass).toHaveBeenCalledWith(this.dummyListItem);
});
it('should call .preventDefault', function () {
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
expect(this.event.preventDefault).toHaveBeenCalled();
});
it('should call .hide', function () {
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
expect(this.dropdown.hide).toHaveBeenCalled();
});
it('should construct CustomEvent', function () {
- expect(window.CustomEvent).toHaveBeenCalledWith('click.dl', jasmine.any(Object));
- });
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
- it('should call .classList.contains checking for IGNORE_CLASS', function () {
- expect(this.classList.contains).toHaveBeenCalledWith(IGNORE_CLASS);
+ expect(window.CustomEvent).toHaveBeenCalledWith('click.dl', jasmine.any(Object));
});
it('should call .dispatchEvent with the customEvent', function () {
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
expect(this.list.dispatchEvent).toHaveBeenCalledWith(this.customEvent);
});
describe('if the target is a UL element', function () {
beforeEach(function () {
- this.event = { preventDefault: () => {}, target: { tagName: 'UL', classList: this.classList } };
-
- spyOn(this.event, 'preventDefault');
- utils.closest.calls.reset();
+ this.event.target = document.createElement('ul');
- DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+ spyOn(this.event.target, 'closest');
});
it('should return immediately', function () {
- expect(utils.closest).not.toHaveBeenCalled();
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
+ expect(this.event.target.closest).not.toHaveBeenCalled();
+ expect(this.dropdown.addSelectedClass).not.toHaveBeenCalled();
});
});
- describe('if the target has the IGNORE_CLASS class', function () {
+ describe('if the target has the droplab-item-ignore class', function () {
beforeEach(function () {
- this.event = { preventDefault: () => {}, target: { tagName: 'LI', classList: this.classList } };
+ this.ignoredButton = document.createElement('button');
+ this.ignoredButton.classList.add('droplab-item-ignore');
+ this.event.target = this.ignoredButton;
- spyOn(this.event, 'preventDefault');
- this.classList.contains.and.returnValue(true);
- utils.closest.calls.reset();
+ spyOn(this.ignoredButton, 'closest').and.callThrough();
+ });
+ it('does not select element', function () {
DropDown.prototype.clickEvent.call(this.dropdown, this.event);
- });
- it('should return immediately', function () {
- expect(utils.closest).not.toHaveBeenCalled();
+ expect(this.ignoredButton.closest.calls.count()).toBe(1);
+ expect(this.ignoredButton.closest).toHaveBeenCalledWith('.droplab-item-ignore');
+ expect(this.dropdown.addSelectedClass).not.toHaveBeenCalled();
});
});
describe('if no selected element exists', function () {
beforeEach(function () {
this.event.preventDefault.calls.reset();
- this.clickEvent = DropDown.prototype.clickEvent.call(this.dropdown, this.event);
- });
-
- it('should return undefined', function () {
- expect(this.clickEvent).toBe(undefined);
+ this.dummyListItem = null;
});
it('should return before .preventDefault is called', function () {
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
expect(this.event.preventDefault).not.toHaveBeenCalled();
+ expect(this.dropdown.addSelectedClass).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('if hideOnClick is false', () => {
+ beforeEach(function () {
+ this.dropdown.hideOnClick = false;
+ this.dropdown.hide.calls.reset();
+ });
+
+ it('should not call .hide', function () {
+ DropDown.prototype.clickEvent.call(this.dropdown, this.event);
+
+ expect(this.dropdown.hide).not.toHaveBeenCalled();
});
});
});
@@ -278,20 +316,23 @@ describe('DropDown', function () {
describe('addEvents', function () {
beforeEach(function () {
- this.list = { addEventListener: () => {} };
+ this.list = {
+ addEventListener: () => {},
+ querySelectorAll: () => [],
+ };
this.dropdown = {
list: this.list,
clickEvent: () => {},
closeDropdown: () => {},
eventWrapper: {},
};
+ });
+ it('should call .addEventListener', function () {
spyOn(this.list, 'addEventListener');
DropDown.prototype.addEvents.call(this.dropdown);
- });
- it('should call .addEventListener', function () {
expect(this.list.addEventListener).toHaveBeenCalledWith('click', jasmine.any(Function));
expect(this.list.addEventListener).toHaveBeenCalledWith('keyup', jasmine.any(Function));
});
diff --git a/spec/javascripts/environments/emtpy_state_spec.js b/spec/javascripts/environments/emtpy_state_spec.js
index 82de35933f5..10a19af4175 100644
--- a/spec/javascripts/environments/emtpy_state_spec.js
+++ b/spec/javascripts/environments/emtpy_state_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import emptyState from '~/environments/components/empty_state.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('environments empty state', () => {
let vm;
diff --git a/spec/javascripts/environments/environment_table_spec.js b/spec/javascripts/environments/environment_table_spec.js
index 9bd42863759..0e5e50a59a5 100644
--- a/spec/javascripts/environments/environment_table_spec.js
+++ b/spec/javascripts/environments/environment_table_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import environmentTableComp from '~/environments/components/environments_table.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Environment table', () => {
let Component;
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index a41a4e5a3f7..5bb37304372 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -1,9 +1,9 @@
import _ from 'underscore';
import Vue from 'vue';
import environmentsComponent from '~/environments/components/environments_app.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
import { environment, folder } from './mock_data';
-import { headersInterceptor } from '../helpers/vue_resource_helper';
-import mountComponent from '../helpers/vue_mount_component_helper';
describe('Environment', () => {
const mockData = {
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index a085074d312..906a1116974 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -1,9 +1,9 @@
import _ from 'underscore';
import Vue from 'vue';
import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
+import { headersInterceptor } from 'spec/helpers/vue_resource_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { environmentsList } from '../mock_data';
-import { headersInterceptor } from '../../helpers/vue_resource_helper';
-import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Environments Folder View', () => {
let Component;
diff --git a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
index 34ffc7b1016..1b1f28f3ddb 100644
--- a/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
+++ b/spec/javascripts/feature_highlight/feature_highlight_helper_spec.js
@@ -8,7 +8,7 @@ import {
mouseenter,
inserted,
} from '~/feature_highlight/feature_highlight_helper';
-import getSetTimeoutPromise from '../helpers/set_timeout_promise_helper';
+import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
describe('feature highlight helper', () => {
describe('getSelector', () => {
diff --git a/spec/javascripts/feature_highlight/feature_highlight_spec.js b/spec/javascripts/feature_highlight/feature_highlight_spec.js
index 6e1b0429ab7..f3f80cb3771 100644
--- a/spec/javascripts/feature_highlight/feature_highlight_spec.js
+++ b/spec/javascripts/feature_highlight/feature_highlight_spec.js
@@ -1,11 +1,13 @@
import * as featureHighlightHelper from '~/feature_highlight/feature_highlight_helper';
import * as featureHighlight from '~/feature_highlight/feature_highlight';
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
describe('feature highlight', () => {
beforeEach(() => {
setFixtures(`
<div>
- <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" disabled>
+ <div class="js-feature-highlight" data-highlight="test" data-highlight-priority="10" data-dismiss-endpoint="/test" disabled>
Trigger
</div>
</div>
@@ -19,13 +21,21 @@ describe('feature highlight', () => {
});
describe('setupFeatureHighlightPopover', () => {
+ let mock;
const selector = '.js-feature-highlight[data-highlight=test]';
+
beforeEach(() => {
+ mock = new MockAdapter(axios);
+ mock.onGet('/test').reply(200);
spyOn(window, 'addEventListener');
spyOn(window, 'removeEventListener');
featureHighlight.setupFeatureHighlightPopover('test', 0);
});
+ afterEach(() => {
+ mock.restore();
+ });
+
it('setup popover content', () => {
const $popoverContent = $('.feature-highlight-popover-content');
const outerHTML = $popoverContent.prop('outerHTML');
@@ -51,15 +61,6 @@ describe('feature highlight', () => {
}, 0);
});
- it('setup inserted.bs.popover', () => {
- $(selector).trigger('mouseenter');
- const popoverId = $(selector).attr('aria-describedby');
- const spyEvent = spyOnEvent(`#${popoverId} .dismiss-feature-highlight`, 'click');
-
- $(`#${popoverId} .dismiss-feature-highlight`).click();
- expect(spyEvent).toHaveBeenTriggered();
- });
-
it('setup show.bs.popover', () => {
$(selector).trigger('show.bs.popover');
expect(window.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function));
@@ -75,9 +76,19 @@ describe('feature highlight', () => {
});
it('displays popover', () => {
- expect($(selector).attr('aria-describedby')).toBeFalsy();
+ expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeFalsy();
$(selector).trigger('mouseenter');
- expect($(selector).attr('aria-describedby')).toBeTruthy();
+ expect(document.querySelector(selector).getAttribute('aria-describedby')).toBeTruthy();
+ });
+
+ it('toggles when clicked', () => {
+ $(selector).trigger('mouseenter');
+ const popoverId = $(selector).attr('aria-describedby');
+ const toggleSpy = spyOn(featureHighlightHelper.togglePopover, 'call');
+
+ $(`#${popoverId} .dismiss-feature-highlight`).click();
+
+ expect(toggleSpy).toHaveBeenCalled();
});
});
diff --git a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
index 4a516c517ef..59bd2650081 100644
--- a/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
+++ b/spec/javascripts/filtered_search/components/recent_searches_dropdown_content_spec.js
@@ -1,7 +1,6 @@
import Vue from 'vue';
import eventHub from '~/filtered_search/event_hub';
-import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content';
-
+import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content.vue';
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
const createComponent = (propsData) => {
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index f1e6119253e..c37a964975d 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -1,8 +1,6 @@
-import '~/filtered_search/dropdown_utils';
-import '~/filtered_search/filtered_search_tokenizer';
-import '~/filtered_search/filtered_search_dropdown';
-import '~/filtered_search/dropdown_user';
-
+import DropdownUtils from '~/filtered_search/dropdown_utils';
+import DropdownUser from '~/filtered_search/dropdown_user';
+import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer';
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
describe('Dropdown User', () => {
@@ -10,18 +8,18 @@ describe('Dropdown User', () => {
let dropdownUser;
beforeEach(() => {
- spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
- spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
- spyOn(gl.DropdownUser.prototype, 'getGroupId').and.callFake(() => {});
- spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
+ spyOn(DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
+ spyOn(DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
+ spyOn(DropdownUser.prototype, 'getGroupId').and.callFake(() => {});
+ spyOn(DropdownUtils, 'getSearchInput').and.callFake(() => {});
- dropdownUser = new gl.DropdownUser({
+ dropdownUser = new DropdownUser({
tokenKeys: FilteredSearchTokenKeys,
});
});
it('should not return the double quote found in value', () => {
- spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({
+ spyOn(FilteredSearchTokenizer, 'processTokens').and.returnValue({
lastToken: '"johnny appleseed',
});
@@ -29,7 +27,7 @@ describe('Dropdown User', () => {
});
it('should not return the single quote found in value', () => {
- spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.returnValue({
+ spyOn(FilteredSearchTokenizer, 'processTokens').and.returnValue({
lastToken: '\'larry boy',
});
@@ -39,22 +37,22 @@ describe('Dropdown User', () => {
describe('config AjaxFilter\'s endpoint', () => {
beforeEach(() => {
- spyOn(gl.DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
- spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
- spyOn(gl.DropdownUser.prototype, 'getGroupId').and.callFake(() => {});
+ spyOn(DropdownUser.prototype, 'bindEvents').and.callFake(() => {});
+ spyOn(DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
+ spyOn(DropdownUser.prototype, 'getGroupId').and.callFake(() => {});
});
it('should return endpoint', () => {
window.gon = {
relative_url_root: '',
};
- const dropdown = new gl.DropdownUser();
+ const dropdown = new DropdownUser();
expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
});
it('should return endpoint when relative_url_root is undefined', () => {
- const dropdown = new gl.DropdownUser();
+ const dropdown = new DropdownUser();
expect(dropdown.config.AjaxFilter.endpoint).toBe('/autocomplete/users.json');
});
@@ -63,7 +61,7 @@ describe('Dropdown User', () => {
window.gon = {
relative_url_root: '/gitlab_directory',
};
- const dropdown = new gl.DropdownUser();
+ const dropdown = new DropdownUser();
expect(dropdown.config.AjaxFilter.endpoint).toBe('/gitlab_directory/autocomplete/users.json');
});
@@ -84,7 +82,7 @@ describe('Dropdown User', () => {
loadFixtures(fixtureTemplate);
authorFilterDropdownElement = document.querySelector('#js-dropdown-author');
const dummyInput = document.createElement('div');
- dropdown = new gl.DropdownUser({
+ dropdown = new DropdownUser({
dropdown: authorFilterDropdownElement,
input: dummyInput,
});
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index d6e1af105f1..3d6dec19eca 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -1,6 +1,5 @@
-import '~/filtered_search/dropdown_utils';
-import '~/filtered_search/filtered_search_tokenizer';
-import '~/filtered_search/filtered_search_dropdown_manager';
+import DropdownUtils from '~/filtered_search/dropdown_utils';
+import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
@@ -10,25 +9,25 @@ describe('Dropdown Utils', () => {
describe('getEscapedText', () => {
it('should return same word when it has no space', () => {
- const escaped = gl.DropdownUtils.getEscapedText('textWithoutSpace');
+ const escaped = DropdownUtils.getEscapedText('textWithoutSpace');
expect(escaped).toBe('textWithoutSpace');
});
it('should escape with double quotes', () => {
- let escaped = gl.DropdownUtils.getEscapedText('text with space');
+ let escaped = DropdownUtils.getEscapedText('text with space');
expect(escaped).toBe('"text with space"');
- escaped = gl.DropdownUtils.getEscapedText('won\'t fix');
+ escaped = DropdownUtils.getEscapedText('won\'t fix');
expect(escaped).toBe('"won\'t fix"');
});
it('should escape with single quotes', () => {
- const escaped = gl.DropdownUtils.getEscapedText('won"t fix');
+ const escaped = DropdownUtils.getEscapedText('won"t fix');
expect(escaped).toBe('\'won"t fix\'');
});
it('should escape with single quotes by default', () => {
- const escaped = gl.DropdownUtils.getEscapedText('won"t\' fix');
+ const escaped = DropdownUtils.getEscapedText('won"t\' fix');
expect(escaped).toBe('\'won"t\' fix\'');
});
});
@@ -50,14 +49,14 @@ describe('Dropdown Utils', () => {
it('should filter without symbol', () => {
input.value = 'roo';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
+ const updatedItem = DropdownUtils.filterWithSymbol('@', input, item);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with symbol', () => {
input.value = '@roo';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('@', input, item);
+ const updatedItem = DropdownUtils.filterWithSymbol('@', input, item);
expect(updatedItem.droplab_hidden).toBe(false);
});
@@ -69,56 +68,56 @@ describe('Dropdown Utils', () => {
it('should filter with double quote', () => {
input.value = '"';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote and symbol', () => {
input.value = '~"';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote and multiple words', () => {
input.value = '"community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with double quote, symbol and multiple words', () => {
input.value = '~"community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote', () => {
input.value = '\'';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and symbol', () => {
input.value = '~\'';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote and multiple words', () => {
input.value = '\'community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should filter with single quote, symbol and multiple words', () => {
input.value = '~\'community con';
- const updatedItem = gl.DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
+ const updatedItem = DropdownUtils.filterWithSymbol('~', input, multipleWordItem);
expect(updatedItem.droplab_hidden).toBe(false);
});
});
@@ -150,26 +149,26 @@ describe('Dropdown Utils', () => {
it('should filter', () => {
input.value = 'l';
- let updatedItem = gl.DropdownUtils.filterHint(config(), {
+ let updatedItem = DropdownUtils.filterHint(config(), {
hint: 'label',
});
expect(updatedItem.droplab_hidden).toBe(false);
input.value = 'o';
- updatedItem = gl.DropdownUtils.filterHint(config(), {
+ updatedItem = DropdownUtils.filterHint(config(), {
hint: 'label',
});
expect(updatedItem.droplab_hidden).toBe(true);
});
it('should return droplab_hidden false when item has no hint', () => {
- const updatedItem = gl.DropdownUtils.filterHint(config(), {}, '');
+ const updatedItem = DropdownUtils.filterHint(config(), {}, '');
expect(updatedItem.droplab_hidden).toBe(false);
});
it('should allow multiple if item.type is array', () => {
input.value = 'label:~first la';
- const updatedItem = gl.DropdownUtils.filterHint(config(), {
+ const updatedItem = DropdownUtils.filterHint(config(), {
hint: 'label',
type: 'array',
});
@@ -178,12 +177,12 @@ describe('Dropdown Utils', () => {
it('should prevent multiple if item.type is not array', () => {
input.value = 'milestone:~first mile';
- let updatedItem = gl.DropdownUtils.filterHint(config(), {
+ let updatedItem = DropdownUtils.filterHint(config(), {
hint: 'milestone',
});
expect(updatedItem.droplab_hidden).toBe(true);
- updatedItem = gl.DropdownUtils.filterHint(config(), {
+ updatedItem = DropdownUtils.filterHint(config(), {
hint: 'milestone',
type: 'string',
});
@@ -205,7 +204,7 @@ describe('Dropdown Utils', () => {
color: '#000000',
};
- const updated = gl.DropdownUtils.mergeDuplicateLabels(dataMap, newLabel);
+ const updated = DropdownUtils.mergeDuplicateLabels(dataMap, newLabel);
expect(updated[newLabel.title]).toEqual(newLabel);
});
@@ -215,36 +214,36 @@ describe('Dropdown Utils', () => {
color: '#000000',
};
- const updated = gl.DropdownUtils.mergeDuplicateLabels(dataMap, duplicate);
+ const updated = DropdownUtils.mergeDuplicateLabels(dataMap, duplicate);
expect(updated.label.multipleColors).toEqual([dataMap.label.color, duplicate.color]);
});
});
describe('duplicateLabelColor', () => {
it('should linear-gradient 2 colors', () => {
- const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']);
+ const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']);
expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 50%, #000000 50%, #000000 100%)');
});
it('should linear-gradient 3 colors', () => {
- const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']);
+ const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333']);
expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 33%, #000000 33%, #000000 66%, #333333 66%, #333333 100%)');
});
it('should linear-gradient 4 colors', () => {
- const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD']);
+ const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD']);
expect(gradient).toEqual('linear-gradient(#FFFFFF 0%, #FFFFFF 25%, #000000 25%, #000000 50%, #333333 50%, #333333 75%, #DDDDDD 75%, #DDDDDD 100%)');
});
it('should not linear-gradient more than 4 colors', () => {
- const gradient = gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']);
+ const gradient = DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000', '#333333', '#DDDDDD', '#EEEEEE']);
expect(gradient.indexOf('#EEEEEE') === -1).toEqual(true);
});
});
describe('duplicateLabelPreprocessing', () => {
it('should set preprocessed to true', () => {
- const results = gl.DropdownUtils.duplicateLabelPreprocessing([]);
+ const results = DropdownUtils.duplicateLabelPreprocessing([]);
expect(results.preprocessed).toEqual(true);
});
@@ -256,7 +255,7 @@ describe('Dropdown Utils', () => {
title: 'label2',
color: '#000000',
}];
- const results = gl.DropdownUtils.duplicateLabelPreprocessing(data);
+ const results = DropdownUtils.duplicateLabelPreprocessing(data);
expect(results.length).toEqual(2);
expect(results[0]).toEqual(data[0]);
@@ -271,14 +270,14 @@ describe('Dropdown Utils', () => {
title: 'label',
color: '#000000',
}];
- const results = gl.DropdownUtils.duplicateLabelPreprocessing(data);
+ const results = DropdownUtils.duplicateLabelPreprocessing(data);
it('should merge duplicate labels', () => {
expect(results.length).toEqual(1);
});
it('should convert multiple colored labels into linear-gradient', () => {
- expect(results[0].color).toEqual(gl.DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']));
+ expect(results[0].color).toEqual(DropdownUtils.duplicateLabelColor(['#FFFFFF', '#000000']));
});
it('should set multiple colored label text color to black', () => {
@@ -289,7 +288,7 @@ describe('Dropdown Utils', () => {
describe('setDataValueIfSelected', () => {
beforeEach(() => {
- spyOn(gl.FilteredSearchDropdownManager, 'addWordToInput')
+ spyOn(FilteredSearchDropdownManager, 'addWordToInput')
.and.callFake(() => {});
});
@@ -298,8 +297,8 @@ describe('Dropdown Utils', () => {
getAttribute: () => 'value',
};
- gl.DropdownUtils.setDataValueIfSelected(null, selected);
- expect(gl.FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1);
+ DropdownUtils.setDataValueIfSelected(null, selected);
+ expect(FilteredSearchDropdownManager.addWordToInput.calls.count()).toEqual(1);
});
it('returns true when dataValue exists', () => {
@@ -307,7 +306,7 @@ describe('Dropdown Utils', () => {
getAttribute: () => 'value',
};
- const result = gl.DropdownUtils.setDataValueIfSelected(null, selected);
+ const result = DropdownUtils.setDataValueIfSelected(null, selected);
expect(result).toBe(true);
});
@@ -316,7 +315,7 @@ describe('Dropdown Utils', () => {
getAttribute: () => null,
};
- const result = gl.DropdownUtils.setDataValueIfSelected(null, selected);
+ const result = DropdownUtils.setDataValueIfSelected(null, selected);
expect(result).toBe(false);
});
});
@@ -326,7 +325,7 @@ describe('Dropdown Utils', () => {
const value = 'label:none ';
it('should return selectionStart when cursor is at the trailing space', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 11,
value,
});
@@ -336,7 +335,7 @@ describe('Dropdown Utils', () => {
});
it('should return input when cursor is at the start of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 0,
value,
});
@@ -346,7 +345,7 @@ describe('Dropdown Utils', () => {
});
it('should return input when cursor is at the middle of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 7,
value,
});
@@ -356,7 +355,7 @@ describe('Dropdown Utils', () => {
});
it('should return input when cursor is at the end of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 10,
value,
});
@@ -370,7 +369,7 @@ describe('Dropdown Utils', () => {
const value = 'label:~"Community Contribution"';
it('should return input when cursor is after the first word', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 17,
value,
});
@@ -380,7 +379,7 @@ describe('Dropdown Utils', () => {
});
it('should return input when cursor is before the second word', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 18,
value,
});
@@ -394,7 +393,7 @@ describe('Dropdown Utils', () => {
const value = 'label:~"Community Contribution';
it('should return entire input when cursor is at the start of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 0,
value,
});
@@ -404,7 +403,7 @@ describe('Dropdown Utils', () => {
});
it('should return entire input when cursor is at the end of input', () => {
- const { left, right } = gl.DropdownUtils.getInputSelectionPosition({
+ const { left, right } = DropdownUtils.getInputSelectionPosition({
selectionStart: 30,
value,
});
@@ -434,7 +433,7 @@ describe('Dropdown Utils', () => {
const valueContainer = authorToken.querySelector('.value-container');
valueContainer.dataset.originalValue = originalValue;
- const searchQuery = gl.DropdownUtils.getSearchQuery();
+ const searchQuery = DropdownUtils.getSearchQuery();
expect(searchQuery).toBe(' search term author:original dance');
});
diff --git a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
index 5c7e9115aac..71c14582329 100644
--- a/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js
@@ -1,6 +1,4 @@
-import '~/filtered_search/filtered_search_visual_tokens';
-import '~/filtered_search/filtered_search_tokenizer';
-import '~/filtered_search/filtered_search_dropdown_manager';
+import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
describe('Filtered Search Dropdown Manager', () => {
beforeEach(() => {
@@ -28,7 +26,7 @@ describe('Filtered Search Dropdown Manager', () => {
describe('input has no existing value', () => {
it('should add just tokenName', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('milestone');
+ FilteredSearchDropdownManager.addWordToInput('milestone');
const token = document.querySelector('.tokens-container .js-visual-token');
@@ -38,7 +36,7 @@ describe('Filtered Search Dropdown Manager', () => {
});
it('should add tokenName and tokenValue', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('label');
+ FilteredSearchDropdownManager.addWordToInput('label');
let token = document.querySelector('.tokens-container .js-visual-token');
@@ -46,9 +44,9 @@ describe('Filtered Search Dropdown Manager', () => {
expect(token.querySelector('.name').innerText).toBe('label');
expect(getInputValue()).toBe('');
- gl.FilteredSearchDropdownManager.addWordToInput('label', 'none');
+ FilteredSearchDropdownManager.addWordToInput('label', 'none');
// We have to get that reference again
- // Because gl.FilteredSearchDropdownManager deletes the previous token
+ // Because FilteredSearchDropdownManager deletes the previous token
token = document.querySelector('.tokens-container .js-visual-token');
expect(token.classList.contains('filtered-search-token')).toEqual(true);
@@ -61,7 +59,7 @@ describe('Filtered Search Dropdown Manager', () => {
describe('input has existing value', () => {
it('should be able to just add tokenName', () => {
setInputValue('a');
- gl.FilteredSearchDropdownManager.addWordToInput('author');
+ FilteredSearchDropdownManager.addWordToInput('author');
const token = document.querySelector('.tokens-container .js-visual-token');
@@ -71,10 +69,10 @@ describe('Filtered Search Dropdown Manager', () => {
});
it('should replace tokenValue', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('author');
+ FilteredSearchDropdownManager.addWordToInput('author');
setInputValue('roo');
- gl.FilteredSearchDropdownManager.addWordToInput(null, '@root');
+ FilteredSearchDropdownManager.addWordToInput(null, '@root');
const token = document.querySelector('.tokens-container .js-visual-token');
@@ -85,10 +83,10 @@ describe('Filtered Search Dropdown Manager', () => {
});
it('should add tokenValues containing spaces', () => {
- gl.FilteredSearchDropdownManager.addWordToInput('label');
+ FilteredSearchDropdownManager.addWordToInput('label');
setInputValue('"test ');
- gl.FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\'');
+ FilteredSearchDropdownManager.addWordToInput('label', '~\'"test me"\'');
const token = document.querySelector('.tokens-container .js-visual-token');
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 0ed9a587dc1..95d02974bdc 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -5,9 +5,10 @@ import RecentSearchesServiceError from '~/filtered_search/services/recent_search
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
import '~/lib/utils/common_utils';
-import '~/filtered_search/filtered_search_tokenizer';
-import '~/filtered_search/filtered_search_dropdown_manager';
-import '~/filtered_search/filtered_search_manager';
+import DropdownUtils from '~/filtered_search/dropdown_utils';
+import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
+import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager';
+import FilteredSearchManager from '~/filtered_search/filtered_search_manager';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Filtered Search Manager', () => {
@@ -49,21 +50,21 @@ describe('Filtered Search Manager', () => {
</div>
`);
- spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
+ spyOn(FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
});
const initializeManager = () => {
/* eslint-disable jasmine/no-unsafe-spy */
- spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
- spyOn(gl.FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {});
- spyOn(gl.FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {});
+ spyOn(FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
+ spyOn(FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {});
+ spyOn(FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {});
spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
- spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
/* eslint-enable jasmine/no-unsafe-spy */
input = document.querySelector('.filtered-search');
tokensContainer = document.querySelector('.tokens-container');
- manager = new gl.FilteredSearchManager({ page });
+ manager = new FilteredSearchManager({ page });
manager.setup();
};
@@ -81,7 +82,7 @@ describe('Filtered Search Manager', () => {
});
it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => {
- manager = new gl.FilteredSearchManager({ page });
+ manager = new FilteredSearchManager({ page });
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
@@ -93,7 +94,7 @@ describe('Filtered Search Manager', () => {
describe('setup', () => {
beforeEach(() => {
- manager = new gl.FilteredSearchManager({ page });
+ manager = new FilteredSearchManager({ page });
});
it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
@@ -108,7 +109,7 @@ describe('Filtered Search Manager', () => {
describe('searchState', () => {
beforeEach(() => {
- spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
+ spyOn(FilteredSearchManager.prototype, 'search').and.callFake(() => {});
initializeManager();
});
@@ -134,7 +135,7 @@ describe('Filtered Search Manager', () => {
};
manager.searchState(e);
- expect(gl.FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
+ expect(FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
});
it('should call search when there is state', () => {
@@ -149,7 +150,7 @@ describe('Filtered Search Manager', () => {
};
manager.searchState(e);
- expect(gl.FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
+ expect(FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
});
});
@@ -251,44 +252,44 @@ describe('Filtered Search Manager', () => {
});
it('removes last token', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
dispatchBackspaceEvent(input, 'keyup');
dispatchBackspaceEvent(input, 'keyup');
- expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
});
it('sets the input', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
dispatchDeleteEvent(input, 'keyup');
dispatchDeleteEvent(input, 'keyup');
- expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
expect(input.value).toEqual('~bug');
});
});
it('does not remove token or change input when there is existing input', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
- spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
input.value = 'text';
dispatchDeleteEvent(input, 'keyup');
- expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
- expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
expect(input.value).toEqual('text');
});
it('does not remove previous token on single backspace press', () => {
- spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
- spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
input.value = 't';
dispatchDeleteEvent(input, 'keyup');
- expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
- expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
expect(input.value).toEqual('t');
});
});
@@ -309,7 +310,7 @@ describe('Filtered Search Manager', () => {
describe('unselected token', () => {
beforeEach(() => {
- spyOn(gl.FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough();
+ spyOn(FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough();
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none'),
@@ -380,16 +381,16 @@ describe('Filtered Search Manager', () => {
describe('removeSelectedToken', () => {
beforeEach(() => {
- spyOn(gl.FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough();
- spyOn(gl.FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough();
- spyOn(gl.FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough();
+ spyOn(FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough();
+ spyOn(FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough();
+ spyOn(FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough();
initializeManager();
});
it('calls FilteredSearchVisualTokens.removeSelectedToken', () => {
manager.removeSelectedToken();
- expect(gl.FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled();
+ expect(FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled();
});
it('calls handleInputPlaceholder', () => {
@@ -421,12 +422,12 @@ describe('Filtered Search Manager', () => {
manager.filteredSearchInput.value = inputValue;
manager.filteredSearchInput.dispatchEvent(new Event('input'));
- expect(gl.DropdownUtils.getSearchQuery()).toEqual(inputValue);
+ expect(DropdownUtils.getSearchQuery()).toEqual(inputValue);
manager.clearSearchButton.click();
expect(manager.filteredSearchInput.value).toEqual('');
- expect(gl.DropdownUtils.getSearchQuery()).toEqual('');
+ expect(DropdownUtils.getSearchQuery()).toEqual('');
});
});
diff --git a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
index bf8b66f1110..465f5f79931 100644
--- a/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js
@@ -1,19 +1,19 @@
import FilteredSearchTokenKeys from '~/filtered_search/filtered_search_token_keys';
-import '~/filtered_search/filtered_search_tokenizer';
+import FilteredSearchTokenizer from '~/filtered_search/filtered_search_tokenizer';
describe('Filtered Search Tokenizer', () => {
const allowedKeys = FilteredSearchTokenKeys.getKeys();
describe('processTokens', () => {
it('returns for input containing only search value', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys);
expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(0);
expect(results.lastToken).toBe(results.searchToken);
});
it('returns for input containing only tokens', () => {
- const results = gl.FilteredSearchTokenizer
+ const results = FilteredSearchTokenizer
.processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none', allowedKeys);
expect(results.searchToken).toBe('');
expect(results.tokens.length).toBe(4);
@@ -37,7 +37,7 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input starting with search value and ending with tokens', () => {
- const results = gl.FilteredSearchTokenizer
+ const results = FilteredSearchTokenizer
.processTokens('searchTerm anotherSearchTerm milestone:none', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(1);
@@ -48,7 +48,7 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input starting with tokens and ending with search value', () => {
- const results = gl.FilteredSearchTokenizer
+ const results = FilteredSearchTokenizer
.processTokens('assignee:@user searchTerm', allowedKeys);
expect(results.searchToken).toBe('searchTerm');
@@ -60,7 +60,7 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input containing search value wrapped between tokens', () => {
- const results = gl.FilteredSearchTokenizer
+ const results = FilteredSearchTokenizer
.processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
@@ -81,7 +81,7 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns for input containing search value in between tokens', () => {
- const results = gl.FilteredSearchTokenizer
+ const results = FilteredSearchTokenizer
.processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3);
@@ -101,14 +101,14 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns search value for invalid tokens', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('fake:token', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens('fake:token', allowedKeys);
expect(results.lastToken).toBe('fake:token');
expect(results.searchToken).toBe('fake:token');
expect(results.tokens.length).toEqual(0);
});
it('returns search value and token for mix of valid and invalid tokens', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys);
expect(results.tokens.length).toEqual(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('real');
@@ -118,13 +118,13 @@ describe('Filtered Search Tokenizer', () => {
});
it('returns search value for invalid symbols', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('std::includes', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens('std::includes', allowedKeys);
expect(results.lastToken).toBe('std::includes');
expect(results.searchToken).toBe('std::includes');
});
it('removes duplicated values', () => {
- const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys);
+ const results = FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys);
expect(results.tokens.length).toBe(1);
expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('foo');
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 0684c3498a2..f1da5f81c0f 100644
--- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
@@ -2,11 +2,12 @@ import _ from 'underscore';
import AjaxCache from '~/lib/utils/ajax_cache';
import UsersCache from '~/lib/utils/users_cache';
-import '~/filtered_search/filtered_search_visual_tokens';
+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', () => {
- const subject = gl.FilteredSearchVisualTokens;
+ const subject = FilteredSearchVisualTokens;
const findElements = (tokenElement) => {
const tokenNameElement = tokenElement.querySelector('.name');
@@ -860,25 +861,25 @@ describe('Filtered Search Visual Tokens', () => {
it('does not preprocess more than once', () => {
let labels = [];
- spyOn(gl.DropdownUtils, 'duplicateLabelPreprocessing').and.callFake(() => []);
+ spyOn(DropdownUtils, 'duplicateLabelPreprocessing').and.callFake(() => []);
- labels = gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
- gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
+ labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
+ FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
- expect(gl.DropdownUtils.duplicateLabelPreprocessing.calls.count()).toEqual(1);
+ expect(DropdownUtils.duplicateLabelPreprocessing.calls.count()).toEqual(1);
});
describe('not preprocessed before', () => {
it('returns preprocessed labels', () => {
let labels = [];
expect(labels.preprocessed).not.toEqual(true);
- labels = gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
+ labels = FilteredSearchVisualTokens.preprocessLabel(endpoint, labels);
expect(labels.preprocessed).toEqual(true);
});
it('overrides AjaxCache with preprocessed results', () => {
spyOn(AjaxCache, 'override').and.callFake(() => {});
- gl.FilteredSearchVisualTokens.preprocessLabel(endpoint, []);
+ FilteredSearchVisualTokens.preprocessLabel(endpoint, []);
expect(AjaxCache.override.calls.count()).toEqual(1);
});
});
@@ -926,7 +927,7 @@ describe('Filtered Search Visual Tokens', () => {
};
const findLabel = tokenValue => labelData.find(
- label => tokenValue === `~${gl.DropdownUtils.getEscapedText(label.title)}`,
+ label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`,
);
it('updates the color of a label token', (done) => {
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index b13d1bf8dff..67b854f61c0 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -64,8 +64,8 @@ describe('glDropdown', function describeDropdown() {
});
afterEach(() => {
- $('body').unbind('keydown');
- this.dropdownContainerElement.unbind('keyup');
+ $('body').off('keydown');
+ this.dropdownContainerElement.off('keyup');
});
it('should open on click', () => {
diff --git a/spec/javascripts/gl_form_spec.js b/spec/javascripts/gl_form_spec.js
index 5a8009e57fd..9c1fc0fda9e 100644
--- a/spec/javascripts/gl_form_spec.js
+++ b/spec/javascripts/gl_form_spec.js
@@ -1,10 +1,8 @@
-import Autosize from 'autosize';
+import autosize from 'autosize';
import GLForm from '~/gl_form';
import '~/lib/utils/text_utility';
import '~/lib/utils/common_utils';
-window.autosize = Autosize;
-
describe('GLForm', () => {
describe('when instantiated', function () {
beforeEach((done) => {
@@ -13,14 +11,12 @@ describe('GLForm', () => {
spyOn($.prototype, 'off').and.returnValue(this.textarea);
spyOn($.prototype, 'on').and.returnValue(this.textarea);
spyOn($.prototype, 'css');
- spyOn(window, 'autosize');
- this.glForm = new GLForm(this.form);
+ this.glForm = new GLForm(this.form, false);
setTimeout(() => {
$.prototype.off.calls.reset();
$.prototype.on.calls.reset();
$.prototype.css.calls.reset();
- window.autosize.calls.reset();
done();
});
});
@@ -43,10 +39,6 @@ describe('GLForm', () => {
expect($.prototype.on).toHaveBeenCalledWith('mouseup.autosize', jasmine.any(Function));
});
- it('should autosize the textarea', () => {
- expect(window.autosize).toHaveBeenCalledWith(jasmine.any(Object));
- });
-
it('should set the resize css property to vertical', () => {
expect($.prototype.css).toHaveBeenCalledWith('resize', 'vertical');
});
@@ -74,7 +66,7 @@ describe('GLForm', () => {
spyOn($.prototype, 'data');
spyOn($.prototype, 'outerHeight').and.returnValue(200);
spyOn(window, 'outerHeight').and.returnValue(400);
- spyOn(window.autosize, 'destroy');
+ spyOn(autosize, 'destroy');
this.glForm.destroyAutosize();
});
@@ -88,7 +80,7 @@ describe('GLForm', () => {
});
it('should call autosize destroy', () => {
- expect(window.autosize.destroy).toHaveBeenCalledWith(this.textarea);
+ expect(autosize.destroy).toHaveBeenCalledWith(this.textarea);
});
it('should set the data-height attribute', () => {
@@ -107,9 +99,9 @@ describe('GLForm', () => {
it('should return undefined if the data-height equals the outerHeight', () => {
spyOn($.prototype, 'outerHeight').and.returnValue(200);
spyOn($.prototype, 'data').and.returnValue(200);
- spyOn(window.autosize, 'destroy');
+ spyOn(autosize, 'destroy');
expect(this.glForm.destroyAutosize()).toBeUndefined();
- expect(window.autosize.destroy).not.toHaveBeenCalled();
+ expect(autosize.destroy).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/gpg_badges_spec.js b/spec/javascripts/gpg_badges_spec.js
index 7a826487bf9..5decb5e6bbd 100644
--- a/spec/javascripts/gpg_badges_spec.js
+++ b/spec/javascripts/gpg_badges_spec.js
@@ -1,6 +1,9 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import GpgBadges from '~/gpg_badges';
describe('GpgBadges', () => {
+ let mock;
const dummyCommitSha = 'n0m0rec0ffee';
const dummyBadgeHtml = 'dummy html';
const dummyResponse = {
@@ -11,38 +14,43 @@ describe('GpgBadges', () => {
};
beforeEach(() => {
+ mock = new MockAdapter(axios);
setFixtures(`
+ <form
+ class="commits-search-form" data-signatures-path="/hello" action="/hello"
+ method="get">
+ <input name="utf8" type="hidden" value="✓">
+ <input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
+ </form>
<div class="parent-container">
<div class="js-loading-gpg-badge" data-commit-sha="${dummyCommitSha}"></div>
</div>
`);
});
- it('displays a loading spinner', () => {
- spyOn($, 'get').and.returnValue({
- done() {
- // intentionally left blank
- },
- });
+ afterEach(() => {
+ mock.restore();
+ });
- GpgBadges.fetch();
+ it('displays a loading spinner', (done) => {
+ mock.onGet('/hello').reply(200);
- expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
- const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
- expect(spinners.length).toBe(1);
+ GpgBadges.fetch().then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
+ const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
+ expect(spinners.length).toBe(1);
+ done();
+ }).catch(done.fail);
});
- it('replaces the loading spinner', () => {
- spyOn($, 'get').and.returnValue({
- done(callback) {
- callback(dummyResponse);
- },
- });
-
- GpgBadges.fetch();
+ it('replaces the loading spinner', (done) => {
+ mock.onGet('/hello').reply(200, dummyResponse);
- expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
- const parentContainer = document.querySelector('.parent-container');
- expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ GpgBadges.fetch().then(() => {
+ expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
+ const parentContainer = document.querySelector('.parent-container');
+ expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
+ done();
+ }).catch(done.fail);
});
});
diff --git a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
index 6599839a526..d8a8c8cc260 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_graph_spec.js
@@ -1,7 +1,7 @@
/* eslint-disable quotes, jasmine/no-suite-dupes, vars-on-top, no-var */
import { scaleLinear, scaleTime } from 'd3-scale';
import { timeParse } from 'd3-time-format';
-import { ContributorsGraph, ContributorsMasterGraph } from '~/graphs/stat_graph_contributors_graph';
+import { ContributorsGraph, ContributorsMasterGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
const d3 = { scaleLinear, scaleTime, timeParse };
diff --git a/spec/javascripts/graphs/stat_graph_contributors_spec.js b/spec/javascripts/graphs/stat_graph_contributors_spec.js
index 962423462e7..e03114c1cc5 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_spec.js
@@ -1,5 +1,5 @@
-import ContributorsStatGraph from '~/graphs/stat_graph_contributors';
-import { ContributorsGraph } from '~/graphs/stat_graph_contributors_graph';
+import ContributorsStatGraph from '~/pages/projects/graphs/show/stat_graph_contributors';
+import { ContributorsGraph } from '~/pages/projects/graphs/show/stat_graph_contributors_graph';
import { setLanguage } from '../helpers/locale_helper';
diff --git a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
index 9b47ab62181..22a9afe1a9d 100644
--- a/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
+++ b/spec/javascripts/graphs/stat_graph_contributors_util_spec.js
@@ -1,6 +1,6 @@
/* eslint-disable quotes, no-var, camelcase, object-property-newline, comma-dangle, max-len, vars-on-top, quote-props */
-import ContributorsStatGraphUtil from '~/graphs/stat_graph_contributors_util';
+import ContributorsStatGraphUtil from '~/pages/projects/graphs/show/stat_graph_contributors_util';
describe("ContributorsStatGraphUtil", function () {
describe("#parse_log", function () {
diff --git a/spec/javascripts/groups/components/group_item_spec.js b/spec/javascripts/groups/components/group_item_spec.js
index 618d0022e4f..e3c942597a3 100644
--- a/spec/javascripts/groups/components/group_item_spec.js
+++ b/spec/javascripts/groups/components/group_item_spec.js
@@ -3,10 +3,9 @@ import * as urlUtils from '~/lib/utils/url_utility';
import groupItemComponent from '~/groups/components/group_item.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import eventHub from '~/groups/event_hub';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockParentGroupItem, mockChildren } from '../mock_data';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren[0]) => {
const Component = Vue.extend(groupItemComponent);
diff --git a/spec/javascripts/groups/components/groups_spec.js b/spec/javascripts/groups/components/groups_spec.js
index 90e818c1545..793c4909d89 100644
--- a/spec/javascripts/groups/components/groups_spec.js
+++ b/spec/javascripts/groups/components/groups_spec.js
@@ -4,10 +4,9 @@ import groupsComponent from '~/groups/components/groups.vue';
import groupFolderComponent from '~/groups/components/group_folder.vue';
import groupItemComponent from '~/groups/components/group_item.vue';
import eventHub from '~/groups/event_hub';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockGroups, mockPageInfo } from '../mock_data';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
const createComponent = (searchEmpty = false) => {
const Component = Vue.extend(groupsComponent);
diff --git a/spec/javascripts/groups/components/item_actions_spec.js b/spec/javascripts/groups/components/item_actions_spec.js
index acccbe639c4..15fd37ebcd2 100644
--- a/spec/javascripts/groups/components/item_actions_spec.js
+++ b/spec/javascripts/groups/components/item_actions_spec.js
@@ -2,10 +2,9 @@ import Vue from 'vue';
import itemActionsComponent from '~/groups/components/item_actions.vue';
import eventHub from '~/groups/event_hub';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockParentGroupItem, mockChildren } from '../mock_data';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
const createComponent = (group = mockParentGroupItem, parentGroup = mockChildren[0]) => {
const Component = Vue.extend(itemActionsComponent);
diff --git a/spec/javascripts/groups/components/item_caret_spec.js b/spec/javascripts/groups/components/item_caret_spec.js
index 8faad455825..36f838a104f 100644
--- a/spec/javascripts/groups/components/item_caret_spec.js
+++ b/spec/javascripts/groups/components/item_caret_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import itemCaretComponent from '~/groups/components/item_caret.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const createComponent = (isGroupOpen = false) => {
const Component = Vue.extend(itemCaretComponent);
diff --git a/spec/javascripts/groups/components/item_stats_spec.js b/spec/javascripts/groups/components/item_stats_spec.js
index 55a7a713ca6..ee7ee18259e 100644
--- a/spec/javascripts/groups/components/item_stats_spec.js
+++ b/spec/javascripts/groups/components/item_stats_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import itemStatsComponent from '~/groups/components/item_stats.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import {
mockParentGroupItem,
ITEM_TYPE,
@@ -9,8 +10,6 @@ import {
PROJECT_VISIBILITY_TYPE,
} from '../mock_data';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
const createComponent = (item = mockParentGroupItem) => {
const Component = Vue.extend(itemStatsComponent);
diff --git a/spec/javascripts/groups/components/item_stats_value_spec.js b/spec/javascripts/groups/components/item_stats_value_spec.js
index e990870aaa6..5e35ae4d36c 100644
--- a/spec/javascripts/groups/components/item_stats_value_spec.js
+++ b/spec/javascripts/groups/components/item_stats_value_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import itemStatsValueComponent from '~/groups/components/item_stats_value.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const createComponent = ({ title, cssClass, iconName, tooltipPlacement, value }) => {
const Component = Vue.extend(itemStatsValueComponent);
diff --git a/spec/javascripts/groups/components/item_type_icon_spec.js b/spec/javascripts/groups/components/item_type_icon_spec.js
index 495cc97b475..24380689b29 100644
--- a/spec/javascripts/groups/components/item_type_icon_spec.js
+++ b/spec/javascripts/groups/components/item_type_icon_spec.js
@@ -1,10 +1,9 @@
import Vue from 'vue';
import itemTypeIconComponent from '~/groups/components/item_type_icon.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { ITEM_TYPE } from '../mock_data';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
const createComponent = (itemType = ITEM_TYPE.GROUP, isGroupOpen = false) => {
const Component = Vue.extend(itemTypeIconComponent);
diff --git a/spec/javascripts/importer_status_spec.js b/spec/javascripts/importer_status_spec.js
new file mode 100644
index 00000000000..71a2cd51f63
--- /dev/null
+++ b/spec/javascripts/importer_status_spec.js
@@ -0,0 +1,107 @@
+import { ImporterStatus } from '~/importer_status';
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
+
+describe('Importer Status', () => {
+ let instance;
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('addToImport', () => {
+ const importUrl = '/import_url';
+
+ beforeEach(() => {
+ setFixtures(`
+ <tr id="repo_123">
+ <td class="import-target"></td>
+ <td class="import-actions job-status">
+ <button name="button" type="submit" class="btn btn-import js-add-to-import">
+ </button>
+ </td>
+ </tr>
+ `);
+ spyOn(ImporterStatus.prototype, 'initStatusPage').and.callFake(() => {});
+ spyOn(ImporterStatus.prototype, 'setAutoUpdate').and.callFake(() => {});
+ instance = new ImporterStatus('', importUrl);
+ });
+
+ it('sets table row to active after post request', (done) => {
+ mock.onPost(importUrl).reply(200, {
+ id: 1,
+ full_path: '/full_path',
+ });
+
+ instance.addToImport({
+ currentTarget: document.querySelector('.js-add-to-import'),
+ })
+ .then(() => {
+ expect(document.querySelector('tr').classList.contains('active')).toEqual(true);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('autoUpdate', () => {
+ const jobsUrl = '/jobs_url';
+
+ beforeEach(() => {
+ const div = document.createElement('div');
+ div.innerHTML = `
+ <div id="project_1">
+ <div class="job-status">
+ </div>
+ </div>
+ `;
+
+ document.body.appendChild(div);
+
+ spyOn(ImporterStatus.prototype, 'initStatusPage').and.callFake(() => {});
+ spyOn(ImporterStatus.prototype, 'setAutoUpdate').and.callFake(() => {});
+ instance = new ImporterStatus(jobsUrl);
+ });
+
+ function setupMock(importStatus) {
+ mock.onGet(jobsUrl).reply(200, [{
+ id: 1,
+ import_status: importStatus,
+ }]);
+ }
+
+ function expectJobStatus(done, status) {
+ instance.autoUpdate()
+ .then(() => {
+ expect(document.querySelector('#project_1').innerText.trim()).toEqual(status);
+ done();
+ })
+ .catch(done.fail);
+ }
+
+ it('sets the job status to done', (done) => {
+ setupMock('finished');
+ expectJobStatus(done, 'done');
+ });
+
+ it('sets the job status to scheduled', (done) => {
+ setupMock('scheduled');
+ expectJobStatus(done, 'scheduled');
+ });
+
+ it('sets the job status to started', (done) => {
+ setupMock('started');
+ expectJobStatus(done, 'started');
+ });
+
+ it('sets the job status to custom status', (done) => {
+ setupMock('custom status');
+ expectJobStatus(done, 'custom status');
+ });
+ });
+});
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
index 8ff93c4f918..365e9fe6a4b 100644
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ b/spec/javascripts/issuable_time_tracker_spec.js
@@ -2,7 +2,7 @@
import Vue from 'vue';
-import timeTracker from '~/sidebar/components/time_tracking/time_tracker';
+import timeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
function initTimeTrackingComponent(opts) {
setFixtures(`
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 1c9f48028f2..584db6c6632 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -6,8 +6,8 @@ import '~/render_gfm';
import * as urlUtils from '~/lib/utils/url_utility';
import issuableApp from '~/issue_show/components/app.vue';
import eventHub from '~/issue_show/event_hub';
+import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import issueShowData from '../mock_data';
-import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
function formatText(text) {
return text.trim().replace(/\s\s+/g, ' ');
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 0da25bdca9c..ff7f99eec14 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import descriptionComponent from '~/issue_show/components/description.vue';
import * as taskList from '~/task_list';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Description component', () => {
let vm;
diff --git a/spec/javascripts/jobs/header_spec.js b/spec/javascripts/jobs/header_spec.js
index a9df0418d5d..0961605ce5c 100644
--- a/spec/javascripts/jobs/header_spec.js
+++ b/spec/javascripts/jobs/header_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import headerComponent from '~/jobs/components/header.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Job details header', () => {
let HeaderComponent;
diff --git a/spec/javascripts/merge_request_notes_spec.js b/spec/javascripts/merge_request_notes_spec.js
index 5d0ee91d977..0d16b23302f 100644
--- a/spec/javascripts/merge_request_notes_spec.js
+++ b/spec/javascripts/merge_request_notes_spec.js
@@ -23,7 +23,7 @@ describe('Merge request notes', () => {
gl.utils.disableButtonIfEmptyField = _.noop;
window.project_uploads_path = 'http://test.host/uploads';
$('body').attr('data-page', 'projects:merge_requests:show');
- window.gon.current_user_id = $('.note:last').data('author-id');
+ window.gon.current_user_id = $('.note:last').data('authorId');
return new Notes('', []);
});
@@ -76,7 +76,7 @@ describe('Merge request notes', () => {
</form>`;
setFixtures(diffsResponse.html + noteFormHtml);
$('body').attr('data-page', 'projects:merge_requests:show');
- window.gon.current_user_id = $('.note:last').data('author-id');
+ window.gon.current_user_id = $('.note:last').data('authorId');
return new Notes('', []);
});
diff --git a/spec/javascripts/notebook/cells/markdown_spec.js b/spec/javascripts/notebook/cells/markdown_spec.js
index 02304bf5d7d..8f8ba231ae8 100644
--- a/spec/javascripts/notebook/cells/markdown_spec.js
+++ b/spec/javascripts/notebook/cells/markdown_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import MarkdownComponent from '~/notebook/cells/markdown.vue';
-import katex from 'vendor/katex';
+import katex from 'katex';
const Component = Vue.extend(MarkdownComponent);
diff --git a/spec/javascripts/notes/helpers.js b/spec/javascripts/notes/helpers.js
new file mode 100644
index 00000000000..a7663710a56
--- /dev/null
+++ b/spec/javascripts/notes/helpers.js
@@ -0,0 +1,12 @@
+// eslint-disable-next-line import/prefer-default-export
+export const resetStore = (store) => {
+ store.replaceState({
+ notes: [],
+ targetNoteHash: null,
+ lastFetchedAt: null,
+
+ notesData: {},
+ userData: {},
+ noteableData: {},
+ });
+};
diff --git a/spec/javascripts/notes/mock_data.js b/spec/javascripts/notes/mock_data.js
index f0c800c759d..ccf4bd070c2 100644
--- a/spec/javascripts/notes/mock_data.js
+++ b/spec/javascripts/notes/mock_data.js
@@ -7,6 +7,8 @@ export const notesDataMock = {
notesPath: '/gitlab-org/gitlab-ce/noteable/issue/98/notes',
quickActionsDocsPath: '/help/user/project/quick_actions',
registerPath: '/users/sign_in?redirect_to_referer=yes#register-pane',
+ closeIssuePath: '/twitter/flight/issues/9.json?issue%5Bstate_event%5D=close',
+ reopenIssuePath: '/twitter/flight/issues/9.json?issue%5Bstate_event%5D=reopen',
};
export const userDataMock = {
diff --git a/spec/javascripts/notes/stores/actions_spec.js b/spec/javascripts/notes/stores/actions_spec.js
index e092320f9a3..ab80ed7bbfb 100644
--- a/spec/javascripts/notes/stores/actions_spec.js
+++ b/spec/javascripts/notes/stores/actions_spec.js
@@ -1,8 +1,16 @@
+import Vue from 'vue';
+import _ from 'underscore';
import * as actions from '~/notes/stores/actions';
+import store from '~/notes/stores';
import testAction from '../../helpers/vuex_action_helper';
+import { resetStore } from '../helpers';
import { discussionMock, notesDataMock, userDataMock, noteableDataMock, individualNote } from '../mock_data';
describe('Actions Notes Store', () => {
+ afterEach(() => {
+ resetStore(store);
+ });
+
describe('setNotesData', () => {
it('should set received notes data', (done) => {
testAction(actions.setNotesData, null, { notesData: {} }, [
@@ -58,4 +66,67 @@ describe('Actions Notes Store', () => {
], done);
});
});
+
+ describe('async methods', () => {
+ const interceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify({}), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(interceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(Vue.http.interceptors, interceptor);
+ });
+
+ describe('closeIssue', () => {
+ it('sets state as closed', (done) => {
+ store.dispatch('closeIssue', { notesData: { closeIssuePath: '' } })
+ .then(() => {
+ expect(store.state.noteableData.state).toEqual('closed');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('reopenIssue', () => {
+ it('sets state as reopened', (done) => {
+ store.dispatch('reopenIssue', { notesData: { reopenIssuePath: '' } })
+ .then(() => {
+ expect(store.state.noteableData.state).toEqual('reopened');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('emitStateChangedEvent', () => {
+ it('emits an event on the document', () => {
+ document.addEventListener('issuable_vue_app:change', (event) => {
+ expect(event.detail.data).toEqual({ id: '1', state: 'closed' });
+ expect(event.detail.isClosed).toEqual(false);
+ });
+
+ store.dispatch('emitStateChangedEvent', { id: '1', state: 'closed' });
+ });
+ });
+
+ describe('toggleIssueLocalState', () => {
+ it('sets issue state as closed', (done) => {
+ testAction(actions.toggleIssueLocalState, 'closed', {}, [
+ { type: 'CLOSE_ISSUE', payload: 'closed' },
+ ], done);
+ });
+
+ it('sets issue state as reopened', (done) => {
+ testAction(actions.toggleIssueLocalState, 'reopened', {}, [
+ { type: 'REOPEN_ISSUE', payload: 'reopened' },
+ ], done);
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
index c5a84b71788..919ffbfdef0 100644
--- a/spec/javascripts/notes/stores/getters_spec.js
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -55,4 +55,10 @@ describe('Getters Notes Store', () => {
expect(getters.getCurrentUserLastNote(state)).toEqual(individualNote.notes[0]);
});
});
+
+ describe('issueState', () => {
+ it('should return the issue state', () => {
+ expect(getters.issueState(state)).toEqual(noteableDataMock.state);
+ });
+ });
});
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 d2386077aa6..349549b9e1f 100644
--- a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
+++ b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
@@ -22,19 +22,19 @@ describe('Abuse Reports', () => {
it('should truncate long messages', () => {
const $longMessage = findMessage('LONG MESSAGE');
- expect($longMessage.data('original-message')).toEqual(jasmine.anything());
+ expect($longMessage.data('originalMessage')).toEqual(jasmine.anything());
assertMaxLength($longMessage);
});
it('should not truncate short messages', () => {
const $shortMessage = findMessage('SHORT MESSAGE');
- expect($shortMessage.data('original-message')).not.toEqual(jasmine.anything());
+ expect($shortMessage.data('originalMessage')).not.toEqual(jasmine.anything());
});
it('should allow clicking a truncated message to expand and collapse the full message', () => {
const $longMessage = findMessage('LONG MESSAGE');
$longMessage.click();
- expect($longMessage.data('original-message').length).toEqual($longMessage.text().length);
+ expect($longMessage.data('originalMessage').length).toEqual($longMessage.text().length);
$longMessage.click();
assertMaxLength($longMessage);
});
diff --git a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
index 440a6585d57..a6fe9fb65e9 100644
--- a/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
+++ b/spec/javascripts/pages/admin/jobs/index/components/stop_jobs_modal_spec.js
@@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import stopJobsModal from '~/pages/admin/jobs/index/components/stop_jobs_modal.vue';
import * as urlUtility from '~/lib/utils/url_utility';
-import mountComponent from '../../../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('stop_jobs_modal.vue', () => {
const props = {
diff --git a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
index 3cd33a3e900..6074e06fcec 100644
--- a/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
+++ b/spec/javascripts/pages/milestones/shared/components/delete_milestone_modal_spec.js
@@ -5,7 +5,7 @@ import deleteMilestoneModal from '~/pages/milestones/shared/components/delete_mi
import eventHub from '~/pages/milestones/shared/event_hub';
import * as urlUtility from '~/lib/utils/url_utility';
-import mountComponent from '../../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('delete_milestone_modal.vue', () => {
const Component = Vue.extend(deleteMilestoneModal);
diff --git a/spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
index 040d14efed2..4655e29eed0 100644
--- a/spec/javascripts/pipeline_schedules/interval_pattern_input_spec.js
+++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
-import IntervalPatternInput from '~/pipeline_schedules/components/interval_pattern_input.vue';
+import IntervalPatternInput from '~/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue';
Vue.use(Translate);
diff --git a/spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
index ed481cb60a1..f95a7cef18a 100644
--- a/spec/javascripts/pipeline_schedules/pipeline_schedule_callout_spec.js
+++ b/spec/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedule_callout_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Cookies from 'js-cookie';
-import PipelineSchedulesCallout from '~/pipeline_schedules/components/pipeline_schedules_callout.vue';
+import PipelineSchedulesCallout from '~/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue';
const PipelineSchedulesCalloutComponent = Vue.extend(PipelineSchedulesCallout);
const cookieKey = 'pipeline_schedules_callout_dismissed';
diff --git a/spec/javascripts/pipelines/async_button_spec.js b/spec/javascripts/pipelines/async_button_spec.js
index 8ce33d410a7..e0ea3649646 100644
--- a/spec/javascripts/pipelines/async_button_spec.js
+++ b/spec/javascripts/pipelines/async_button_spec.js
@@ -15,7 +15,8 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'repeat',
cssClass: 'bar',
- id: 123,
+ pipelineId: 123,
+ type: 'explode',
},
}).$mount();
});
@@ -39,8 +40,9 @@ describe('Pipelines Async Button', () => {
describe('With confirm dialog', () => {
it('should call the service when confimation is positive', () => {
- eventHub.$on('actionConfirmationModal', (data) => {
- expect(data.id).toEqual(123);
+ eventHub.$on('openConfirmationModal', (data) => {
+ expect(data.pipelineId).toEqual(123);
+ expect(data.type).toEqual('explode');
});
component = new AsyncButtonComponent({
@@ -49,7 +51,8 @@ describe('Pipelines Async Button', () => {
title: 'Foo',
icon: 'fa fa-foo',
cssClass: 'bar',
- id: 123,
+ pipelineId: 123,
+ type: 'explode',
},
}).$mount();
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index c3dc7b53d0f..ce181a1e515 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import jobComponent from '~/pipelines/components/graph/job_component.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('pipeline graph job component', () => {
let JobComponent;
diff --git a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js
index bc6413a159f..e58a8018ed5 100644
--- a/spec/javascripts/pipelines/pipeline_details_mediator_spec.js
+++ b/spec/javascripts/pipelines/pipeline_details_mediator_spec.js
@@ -1,6 +1,6 @@
import _ from 'underscore';
import Vue from 'vue';
-import PipelineMediator from '~/pipelines/pipeline_details_mediatior';
+import PipelineMediator from '~/pipelines/pipeline_details_mediator';
describe('PipelineMdediator', () => {
let mediator;
diff --git a/spec/javascripts/pipelines/pipeline_store_spec.js b/spec/javascripts/pipelines/pipeline_store_spec.js
index 85d13445b01..ab2287cc344 100644
--- a/spec/javascripts/pipelines/pipeline_store_spec.js
+++ b/spec/javascripts/pipelines/pipeline_store_spec.js
@@ -8,7 +8,6 @@ describe('Pipeline Store', () => {
});
it('should set defaults', () => {
- expect(store.state).toEqual({ pipeline: {} });
expect(store.state.pipeline).toEqual({});
});
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index a99ebc4e51a..54d5bfd51e6 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -2,7 +2,7 @@ import _ from 'underscore';
import Vue from 'vue';
import pipelinesComp from '~/pipelines/components/pipelines.vue';
import Store from '~/pipelines/stores/pipelines_store';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Pipelines', () => {
const jsonFixtureName = 'pipelines/pipelines.json';
diff --git a/spec/javascripts/profile/account/components/delete_account_modal_spec.js b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
index 588b61196a5..a0939ff5c20 100644
--- a/spec/javascripts/profile/account/components/delete_account_modal_spec.js
+++ b/spec/javascripts/profile/account/components/delete_account_modal_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import deleteAccountModal from '~/profile/account/components/delete_account_modal.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('DeleteAccountModal component', () => {
const actionUrl = `${gl.TEST_HOST}/delete/user`;
diff --git a/spec/javascripts/projects/project_import_gitlab_project_spec.js b/spec/javascripts/projects/project_import_gitlab_project_spec.js
index 2f1aae109e3..126f73103e0 100644
--- a/spec/javascripts/projects/project_import_gitlab_project_spec.js
+++ b/spec/javascripts/projects/project_import_gitlab_project_spec.js
@@ -10,7 +10,7 @@ describe('Import Gitlab project', () => {
<input class="js-path-name" />
`);
- projectImportGitlab.bindEvents();
+ projectImportGitlab();
});
afterEach(() => {
diff --git a/spec/javascripts/projects/project_new_spec.js b/spec/javascripts/projects/project_new_spec.js
index c314ca8ab72..8731ce35d81 100644
--- a/spec/javascripts/projects/project_new_spec.js
+++ b/spec/javascripts/projects/project_new_spec.js
@@ -27,7 +27,7 @@ describe('New Project', () => {
});
it('does not change project path for disabled $projectImportUrl', () => {
- $projectImportUrl.attr('disabled', true);
+ $projectImportUrl.prop('disabled', true);
projectNew.deriveProjectPathFromUrl($projectImportUrl);
@@ -36,7 +36,7 @@ describe('New Project', () => {
describe('for enabled $projectImportUrl', () => {
beforeEach(() => {
- $projectImportUrl.attr('disabled', false);
+ $projectImportUrl.prop('disabled', false);
});
it('does not change project path if it is set by user', () => {
diff --git a/spec/javascripts/projects_dropdown/components/app_spec.js b/spec/javascripts/projects_dropdown/components/app_spec.js
index 42f0f6fc1af..2054fef790b 100644
--- a/spec/javascripts/projects_dropdown/components/app_spec.js
+++ b/spec/javascripts/projects_dropdown/components/app_spec.js
@@ -6,7 +6,7 @@ import eventHub from '~/projects_dropdown/event_hub';
import ProjectsStore from '~/projects_dropdown/store/projects_store';
import ProjectsService from '~/projects_dropdown/service/projects_service';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { currentSession, mockProject, mockRawProject } from '../mock_data';
const createComponent = () => {
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
index fcd0f6a3630..2bafb4e81ca 100644
--- a/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
+++ b/spec/javascripts/projects_dropdown/components/projects_list_frequent_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import projectsListFrequentComponent from '~/projects_dropdown/components/projects_list_frequent.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockFrequents } from '../mock_data';
const createComponent = () => {
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
index edef150dd1e..c193258474e 100644
--- a/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
+++ b/spec/javascripts/projects_dropdown/components/projects_list_item_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import projectsListItemComponent from '~/projects_dropdown/components/projects_list_item.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockProject } from '../mock_data';
const createComponent = () => {
diff --git a/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js b/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
index 67f8a8946c2..c4b86d77034 100644
--- a/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
+++ b/spec/javascripts/projects_dropdown/components/projects_list_search_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import projectsListSearchComponent from '~/projects_dropdown/components/projects_list_search.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { mockProject } from '../mock_data';
const createComponent = () => {
diff --git a/spec/javascripts/projects_dropdown/components/search_spec.js b/spec/javascripts/projects_dropdown/components/search_spec.js
index 24d8a00b254..601264258c2 100644
--- a/spec/javascripts/projects_dropdown/components/search_spec.js
+++ b/spec/javascripts/projects_dropdown/components/search_spec.js
@@ -3,7 +3,7 @@ import Vue from 'vue';
import searchComponent from '~/projects_dropdown/components/search.vue';
import eventHub from '~/projects_dropdown/event_hub';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const createComponent = () => {
const Component = Vue.extend(searchComponent);
diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js
index 6a8a85e3dfb..cf1d0625397 100644
--- a/spec/javascripts/registry/components/app_spec.js
+++ b/spec/javascripts/registry/components/app_spec.js
@@ -1,7 +1,7 @@
import _ from 'underscore';
import Vue from 'vue';
import registry from '~/registry/components/app.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { reposServerResponse } from '../mock_data';
describe('Registry List', () => {
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
index debde1bb357..b509cedbe80 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_collapsed_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import listCollapsed from '~/ide/components/commit_sidebar/list_collapsed.vue';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list collapsed', () => {
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
index 4b20fdf70d6..6f1a1d874d3 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_item_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import listItem from '~/ide/components/commit_sidebar/list_item.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list item', () => {
diff --git a/spec/javascripts/repo/components/commit_sidebar/list_spec.js b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
index cb5240ad118..aeb9de9ace4 100644
--- a/spec/javascripts/repo/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/repo/components/commit_sidebar/list_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import commitSidebarList from '~/ide/components/commit_sidebar/list.vue';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file } from '../../helpers';
describe('Multi-file editor commit sidebar list', () => {
diff --git a/spec/javascripts/repo/components/ide_context_bar_spec.js b/spec/javascripts/repo/components/ide_context_bar_spec.js
index 3f8f37d2343..935da259a99 100644
--- a/spec/javascripts/repo/components/ide_context_bar_spec.js
+++ b/spec/javascripts/repo/components/ide_context_bar_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import ideContextBar from '~/ide/components/ide_context_bar.vue';
-import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
describe('Multi-file editor right context bar', () => {
let vm;
diff --git a/spec/javascripts/repo/components/ide_side_bar_spec.js b/spec/javascripts/repo/components/ide_side_bar_spec.js
index 30e45169205..79c3c8128e8 100644
--- a/spec/javascripts/repo/components/ide_side_bar_spec.js
+++ b/spec/javascripts/repo/components/ide_side_bar_spec.js
@@ -1,8 +1,8 @@
import Vue from 'vue';
import store from '~/ide/stores';
import ideSidebar from '~/ide/components/ide_side_bar.vue';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
-import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
describe('IdeSidebar', () => {
let vm;
diff --git a/spec/javascripts/repo/components/ide_spec.js b/spec/javascripts/repo/components/ide_spec.js
index acfd63eb8de..18135177b5e 100644
--- a/spec/javascripts/repo/components/ide_spec.js
+++ b/spec/javascripts/repo/components/ide_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import ide from '~/ide/components/ide.vue';
-import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file, resetStore } from '../helpers';
describe('ide component', () => {
diff --git a/spec/javascripts/repo/components/new_branch_form_spec.js b/spec/javascripts/repo/components/new_branch_form_spec.js
index cd1d073ec18..82597fc75e8 100644
--- a/spec/javascripts/repo/components/new_branch_form_spec.js
+++ b/spec/javascripts/repo/components/new_branch_form_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import newBranchForm from '~/ide/components/new_branch_form.vue';
-import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../helpers';
describe('Multi-file editor new branch form', () => {
diff --git a/spec/javascripts/repo/components/new_dropdown/index_spec.js b/spec/javascripts/repo/components/new_dropdown/index_spec.js
index 6efbbf6d75e..4a8e4445e2f 100644
--- a/spec/javascripts/repo/components/new_dropdown/index_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/index_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import store from '~/ide/stores';
import newDropdown from '~/ide/components/new_dropdown/index.vue';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../../helpers';
describe('new dropdown component', () => {
diff --git a/spec/javascripts/repo/components/new_dropdown/modal_spec.js b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
index 8bbc3100357..d6a1fdd115c 100644
--- a/spec/javascripts/repo/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/modal_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import store from '~/ide/stores';
import service from '~/ide/services';
import modal from '~/ide/components/new_dropdown/modal.vue';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { file, resetStore } from '../../helpers';
describe('new file modal component', () => {
diff --git a/spec/javascripts/repo/components/new_dropdown/upload_spec.js b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
index 667112ab21a..ee8aab3a252 100644
--- a/spec/javascripts/repo/components/new_dropdown/upload_spec.js
+++ b/spec/javascripts/repo/components/new_dropdown/upload_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import upload from '~/ide/components/new_dropdown/upload.vue';
import store from '~/ide/stores';
import service from '~/ide/services';
-import { createComponentWithStore } from '../../../helpers/vue_mount_component_helper';
+import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { resetStore } from '../../helpers';
describe('new dropdown upload', () => {
diff --git a/spec/javascripts/repo/components/repo_commit_section_spec.js b/spec/javascripts/repo/components/repo_commit_section_spec.js
index 93e94b4f24c..934ada9dec2 100644
--- a/spec/javascripts/repo/components/repo_commit_section_spec.js
+++ b/spec/javascripts/repo/components/repo_commit_section_spec.js
@@ -3,7 +3,7 @@ import * as urlUtils from '~/lib/utils/url_utility';
import store from '~/ide/stores';
import service from '~/ide/services';
import repoCommitSection from '~/ide/components/repo_commit_section.vue';
-import getSetTimeoutPromise from '../../helpers/set_timeout_promise_helper';
+import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
import { file, resetStore } from '../helpers';
describe('RepoCommitSection', () => {
diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js
new file mode 100644
index 00000000000..d433f8c3e07
--- /dev/null
+++ b/spec/javascripts/settings_panels_spec.js
@@ -0,0 +1,29 @@
+import initSettingsPanels from '~/settings_panels';
+
+describe('Settings Panels', () => {
+ preloadFixtures('projects/ci_cd_settings.html.raw');
+
+ beforeEach(() => {
+ loadFixtures('projects/ci_cd_settings.html.raw');
+ });
+
+ describe('initSettingsPane', () => {
+ afterEach(() => {
+ location.hash = '';
+ });
+
+ it('should expand linked hash fragment panel', () => {
+ location.hash = '#js-general-pipeline-settings';
+
+ const pipelineSettingsPanel = document.querySelector('#js-general-pipeline-settings');
+ // Our test environment automatically expands everything so we need to clear that out first
+ pipelineSettingsPanel.classList.remove('expanded');
+
+ expect(pipelineSettingsPanel.classList.contains('expanded')).toBe(false);
+
+ initSettingsPanels();
+
+ expect(pipelineSettingsPanel.classList.contains('expanded')).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js
index b0ea8ae0206..deeea669de8 100644
--- a/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js
+++ b/spec/javascripts/sidebar/lock/edit_form_buttons_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import editFormButtons from '~/sidebar/components/lock/edit_form_buttons.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('EditFormButtons', () => {
let vm1;
diff --git a/spec/javascripts/sidebar/participants_spec.js b/spec/javascripts/sidebar/participants_spec.js
index 30cc549c7c0..2a3b60c399c 100644
--- a/spec/javascripts/sidebar/participants_spec.js
+++ b/spec/javascripts/sidebar/participants_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import participants from '~/sidebar/components/participants/participants.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const PARTICIPANT = {
id: 1,
diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js
index 6bb6d639f24..2fbb7268e0b 100644
--- a/spec/javascripts/sidebar/sidebar_assignees_spec.js
+++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js
@@ -4,8 +4,8 @@ import SidebarAssignees from '~/sidebar/components/assignees/sidebar_assignees';
import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import Mock from './mock_data';
-import mountComponent from '../helpers/vue_mount_component_helper';
describe('sidebar assignees', () => {
let vm;
diff --git a/spec/javascripts/sidebar/sidebar_move_issue_spec.js b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
index 97f762d07a7..0da5d91e376 100644
--- a/spec/javascripts/sidebar/sidebar_move_issue_spec.js
+++ b/spec/javascripts/sidebar/sidebar_move_issue_spec.js
@@ -78,7 +78,7 @@ describe('SidebarMoveIssue', () => {
this.sidebarMoveIssue.onConfirmClicked();
expect(this.mediator.moveIssue).toHaveBeenCalled();
- expect(this.$confirmButton.attr('disabled')).toBe('disabled');
+ expect(this.$confirmButton.prop('disabled')).toBeTruthy();
expect(this.$confirmButton.hasClass('is-loading')).toBe(true);
});
@@ -93,7 +93,7 @@ describe('SidebarMoveIssue', () => {
// Wait for the move issue request to fail
setTimeout(() => {
expect(window.Flash).toHaveBeenCalled();
- expect(this.$confirmButton.attr('disabled')).toBe(undefined);
+ expect(this.$confirmButton.prop('disabled')).toBeFalsy();
expect(this.$confirmButton.hasClass('is-loading')).toBe(false);
done();
});
@@ -120,7 +120,7 @@ describe('SidebarMoveIssue', () => {
this.$content.find('.js-move-issue-dropdown-item').eq(0).trigger('click');
expect(this.mediator.setMoveToProjectId).toHaveBeenCalledWith(0);
- expect(this.$confirmButton.attr('disabled')).toBe('disabled');
+ expect(this.$confirmButton.prop('disabled')).toBeTruthy();
done();
}, 0);
});
diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
index a6113cb0bae..56a2543660b 100644
--- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
+++ b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
@@ -4,7 +4,7 @@ import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
import eventHub from '~/sidebar/event_hub';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import Mock from './mock_data';
describe('Sidebar Subscriptions', function () {
diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js
index 79db05f04ed..aee8f0acbb9 100644
--- a/spec/javascripts/sidebar/subscriptions_spec.js
+++ b/spec/javascripts/sidebar/subscriptions_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import subscriptions from '~/sidebar/components/subscriptions/subscriptions.vue';
-import mountComponent from '../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Subscriptions', function () {
let vm;
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 9b2a5379855..94fcc6c7f2b 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -1,6 +1,6 @@
/* eslint-disable jasmine/no-global-setup */
import $ from 'jquery';
-import 'jasmine-jquery';
+import 'vendor/jasmine-jquery';
import '~/commons';
import Vue from 'vue';
@@ -113,7 +113,9 @@ if (process.env.BABEL_ENV === 'coverage') {
// exempt these files from the coverage report
const troubleMakers = [
'./blob_edit/blob_bundle.js',
- './boards/boards_bundle.js',
+ './boards/components/modal/empty_state.js',
+ './boards/components/modal/footer.js',
+ './boards/components/modal/header.js',
'./cycle_analytics/cycle_analytics_bundle.js',
'./cycle_analytics/components/stage_plan_component.js',
'./cycle_analytics/components/stage_staging_component.js',
@@ -144,6 +146,9 @@ if (process.env.BABEL_ENV === 'coverage') {
describe('Uncovered files', function () {
const sourceFiles = require.context('~', true, /\.js$/);
+
+ $.holdReady(true);
+
sourceFiles.keys().forEach(function (path) {
// ignore if there is a matching spec file
if (testsContext.keys().indexOf(`${path.replace(/\.js$/, '')}_spec`) > -1) {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js
index f14d5f6f76c..db27aa144d6 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import authorComponent from '~/vue_merge_request_widget/components/mr_widget_author.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetAuthor', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js
index 8c55622b15e..6784b498c29 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import authorTimeComponent from '~/vue_merge_request_widget/components/mr_widget_author_time.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetAuthorTime', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index 13e5595bbfc..235c33fac0d 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import headerComponent from '~/vue_merge_request_widget/components/mr_widget_header.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetHeader', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js
index cc43639f576..367c499daaf 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_merge_help_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import mergeHelpComponent from '~/vue_merge_request_widget/components/mr_widget_merge_help.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetMergeHelp', () => {
let vm;
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 d7af956c9c1..431cb7f3913 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,6 @@
import Vue from 'vue';
import pipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mockData from '../mock_data';
describe('MRWidgetPipeline', () => {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
index 66ecaa316c8..b453d180a40 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_rebase_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
import component from '~/vue_merge_request_widget/components/states/mr_widget_rebase.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Merge request widget rebase component', () => {
let Component;
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
index 637bf483deb..5de6ac4079d 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_related_links_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import relatedLinksComponent from '~/vue_merge_request_widget/components/mr_widget_related_links.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetRelatedLinks', () => {
let vm;
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 c39fcda0071..0b25500caf4 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
@@ -1,6 +1,6 @@
import Vue from 'vue';
import mrStatusIcon from '~/vue_merge_request_widget/components/mr_widget_status_icon.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MR widget status icon component', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
index f98ebdb38e6..e818f87b4c8 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_archived_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import archivedComponent from '~/vue_merge_request_widget/components/states/mr_widget_archived.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetArchived', () => {
let vm;
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 95c94e95e3a..d069dc3fcc6 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
@@ -1,7 +1,7 @@
import Vue from 'vue';
import autoMergeFailedComponent from '~/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetAutoMergeFailed', () => {
let vm;
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 658cadddb81..658612aad3c 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
@@ -1,6 +1,6 @@
import Vue from 'vue';
import checkingComponent from '~/vue_merge_request_widget/components/states/mr_widget_checking.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetChecking', () => {
let Component;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
index 51a34739ee9..0e3c134d3ac 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_closed_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import closedComponent from '~/vue_merge_request_widget/components/states/mr_widget_closed.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetClosed', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
index a7d69fdcdb9..5323523abc0 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_conflicts_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import conflictsComponent from '~/vue_merge_request_widget/components/states/mr_widget_conflicts.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetConflicts', () => {
let Component;
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 a57b9811e08..dd1d62cd4ed 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
@@ -1,7 +1,7 @@
import Vue from 'vue';
import failedToMergeComponent from '~/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetFailedToMerge', () => {
let Component;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
index df56c4e2c5c..dd907ad9015 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merge_when_pipeline_succeeds_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import mwpsComponent from '~/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetMergeWhenPipelineSucceeds', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index 43a989393ba..c2c92d8ac56 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetMerged', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
index 0b2ed2d4086..d2d219e4bdb 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merging_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import mergingComponent from '~/vue_merge_request_widget/components/states/mr_widget_merging.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetMerging', () => {
let vm;
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
index 720effb5c1c..34f76b39b28 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_missing_branch_spec.js
@@ -1,38 +1,22 @@
import Vue from 'vue';
-import missingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch';
-
-const createComponent = () => {
- const Component = Vue.extend(missingBranchComponent);
- const mr = {
- sourceBranchRemoved: true,
- };
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr },
- });
-};
+import missingBranchComponent from '~/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetMissingBranch', () => {
- describe('props', () => {
- it('should have props', () => {
- const mrProp = missingBranchComponent.props.mr;
+ let vm;
- expect(mrProp.type instanceof Object).toBeTruthy();
- expect(mrProp.required).toBeTruthy();
- });
+ beforeEach(() => {
+ const Component = Vue.extend(missingBranchComponent);
+ vm = mountComponent(Component, { mr: { sourceBranchRemoved: true } });
});
- describe('components', () => {
- it('should have components added', () => {
- expect(missingBranchComponent.components['mr-widget-merge-help']).toBeDefined();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
describe('missingBranchName', () => {
it('should return proper branch name', () => {
- const vm = createComponent();
expect(vm.missingBranchName).toEqual('source');
vm.mr.sourceBranchRemoved = false;
@@ -43,7 +27,7 @@ describe('MRWidgetMissingBranch', () => {
describe('template', () => {
it('should have correct elements', () => {
- const el = createComponent().$el;
+ const el = vm.$el;
const content = el.textContent.replace(/\n(\s)+/g, ' ').trim();
expect(el.classList.contains('mr-widget-body')).toBeTruthy();
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
index 33f20ab132d..9f8b96c118b 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_not_allowed_spec.js
@@ -1,17 +1,24 @@
import Vue from 'vue';
-import notAllowedComponent from '~/vue_merge_request_widget/components/states/mr_widget_not_allowed';
+import notAllowedComponent from '~/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetNotAllowed', () => {
- describe('template', () => {
+ let vm;
+ beforeEach(() => {
const Component = Vue.extend(notAllowedComponent);
- const vm = new Component({
- el: document.createElement('div'),
- });
- it('should have correct elements', () => {
- expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy();
- expect(vm.$el.innerText).toContain('Ready to be merged automatically.');
- expect(vm.$el.innerText).toContain('Ask someone with write access to this repository to merge this request');
- });
+ vm = mountComponent(Component);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders success icon', () => {
+ expect(vm.$el.querySelector('.ci-status-icon-success')).not.toBe(null);
+ });
+
+ it('renders informative text', () => {
+ expect(vm.$el.innerText).toContain('Ready to be merged automatically.');
+ expect(vm.$el.innerText).toContain('Ask someone with write access to this repository to merge this request');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
index d0702f9f503..baacbc03fb1 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_pipeline_blocked_spec.js
@@ -1,16 +1,23 @@
import Vue from 'vue';
-import pipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked';
+import pipelineBlockedComponent from '~/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetPipelineBlocked', () => {
- describe('template', () => {
+ let vm;
+ beforeEach(() => {
const Component = Vue.extend(pipelineBlockedComponent);
- const vm = new Component({
- el: document.createElement('div'),
- });
- it('should have correct elements', () => {
- expect(vm.$el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(vm.$el.querySelector('button').getAttribute('disabled')).toBeTruthy();
- expect(vm.$el.innerText).toContain('Pipeline blocked. The pipeline for this merge request requires a manual action to proceed');
- });
+ vm = mountComponent(Component);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders warning icon', () => {
+ expect(vm.$el.querySelector('.ci-status-icon-warning')).not.toBe(null);
+ });
+
+ it('renders information text', () => {
+ expect(vm.$el.textContent.trim().replace(/[\r\n]+/g, ' ')).toContain('Pipeline blocked. The pipeline for this merge request requires a manual action to proceed');
});
});
diff --git a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
index cd00d0a39a3..18ba34b55a5 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -3,8 +3,8 @@ import mrWidgetOptions from '~/vue_merge_request_widget/mr_widget_options';
import eventHub from '~/vue_merge_request_widget/event_hub';
import notify from '~/lib/utils/notify';
import { stateKey } from '~/vue_merge_request_widget/stores/state_maps';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
import mockData from './mock_data';
-import mountComponent from '../helpers/vue_mount_component_helper';
const returnPromise = data => new Promise((resolve) => {
resolve({
@@ -295,6 +295,15 @@ describe('mrWidgetOptions', () => {
expect(notify.notifyMe).not.toHaveBeenCalled();
});
+
+ it('should not notify if no pipeline provided', () => {
+ vm.handleNotification({
+ ...data,
+ pipeline: undefined,
+ });
+
+ expect(notify.notifyMe).not.toHaveBeenCalled();
+ });
});
describe('resumePolling', () => {
diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
index 8762ce9903b..668742ebaee 100644
--- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
+++ b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import ciBadge from '~/vue_shared/components/ci_badge_link.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('CI Badge Link Component', () => {
let CIBadge;
diff --git a/spec/javascripts/vue_shared/components/clipboard_button_spec.js b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
index 08e4e1f8337..d0fc10d69ea 100644
--- a/spec/javascripts/vue_shared/components/clipboard_button_spec.js
+++ b/spec/javascripts/vue_shared/components/clipboard_button_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import clipboardButton from '~/vue_shared/components/clipboard_button.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('clipboard button', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/confirmation_input_spec.js b/spec/javascripts/vue_shared/components/confirmation_input_spec.js
deleted file mode 100644
index a6a12614e77..00000000000
--- a/spec/javascripts/vue_shared/components/confirmation_input_spec.js
+++ /dev/null
@@ -1,63 +0,0 @@
-import Vue from 'vue';
-import confirmationInput from '~/vue_shared/components/confirmation_input.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
-
-describe('Confirmation input component', () => {
- const Component = Vue.extend(confirmationInput);
- const props = {
- inputId: 'dummy-id',
- confirmationKey: 'confirmation-key',
- confirmationValue: 'confirmation-value',
- };
- let vm;
-
- afterEach(() => {
- vm.$destroy();
- });
-
- describe('props', () => {
- beforeEach(() => {
- vm = mountComponent(Component, props);
- });
-
- it('sets id of the input field to inputId', () => {
- expect(vm.$refs.enteredValue.id).toBe(props.inputId);
- });
-
- it('sets name of the input field to confirmationKey', () => {
- expect(vm.$refs.enteredValue.name).toBe(props.confirmationKey);
- });
- });
-
- describe('computed', () => {
- describe('inputLabel', () => {
- it('escapes confirmationValue by default', () => {
- vm = mountComponent(Component, { ...props, confirmationValue: 'n<e></e>ds escap"ng' });
- expect(vm.inputLabel).toBe('Type <code>n&lt;e&gt;&lt;/e&gt;ds escap&quot;ng</code> to confirm:');
- });
-
- it('does not escape confirmationValue if escapeValue is false', () => {
- vm = mountComponent(Component, { ...props, confirmationValue: 'n<e></e>ds escap"ng', shouldEscapeConfirmationValue: false });
- expect(vm.inputLabel).toBe('Type <code>n<e></e>ds escap"ng</code> to confirm:');
- });
- });
- });
-
- describe('methods', () => {
- describe('hasCorrectValue', () => {
- beforeEach(() => {
- vm = mountComponent(Component, props);
- });
-
- it('returns false if entered value is incorrect', () => {
- vm.$refs.enteredValue.value = 'incorrect';
- expect(vm.hasCorrectValue()).toBe(false);
- });
-
- it('returns true if entered value is correct', () => {
- vm.$refs.enteredValue.value = props.confirmationValue;
- expect(vm.hasCorrectValue()).toBe(true);
- });
- });
- });
-});
diff --git a/spec/javascripts/vue_shared/components/expand_button_spec.js b/spec/javascripts/vue_shared/components/expand_button_spec.js
index a33ab689dd1..f19589d3b75 100644
--- a/spec/javascripts/vue_shared/components/expand_button_spec.js
+++ b/spec/javascripts/vue_shared/components/expand_button_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import expandButton from '~/vue_shared/components/expand_button.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('expand button', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
index d99b17bdc79..f7581251bf0 100644
--- a/spec/javascripts/vue_shared/components/file_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/file_icon_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import fileIcon from '~/vue_shared/components/file_icon.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('File Icon component', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/gl_modal_spec.js b/spec/javascripts/vue_shared/components/gl_modal_spec.js
new file mode 100644
index 00000000000..2805d9a7003
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/gl_modal_spec.js
@@ -0,0 +1,192 @@
+import Vue from 'vue';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+
+const modalComponent = Vue.extend(GlModal);
+
+describe('GlModal', () => {
+ let vm;
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('props', () => {
+ describe('with id', () => {
+ const props = {
+ id: 'my-modal',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('assigns the id to the modal', () => {
+ expect(vm.$el.id).toBe(props.id);
+ });
+ });
+
+ describe('without id', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, { });
+ });
+
+ it('does not add an id attribute to the modal', () => {
+ expect(vm.$el.hasAttribute('id')).toBe(false);
+ });
+ });
+
+ describe('with headerTitleText', () => {
+ const props = {
+ headerTitleText: 'my title text',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('sets the modal title', () => {
+ const modalTitle = vm.$el.querySelector('.modal-title');
+ expect(modalTitle.innerHTML.trim()).toBe(props.headerTitleText);
+ });
+ });
+
+ describe('with footerPrimaryButtonVariant', () => {
+ const props = {
+ footerPrimaryButtonVariant: 'danger',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('sets the primary button class', () => {
+ const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type');
+ expect(primaryButton).toHaveClass(`btn-${props.footerPrimaryButtonVariant}`);
+ });
+ });
+
+ describe('with footerPrimaryButtonText', () => {
+ const props = {
+ footerPrimaryButtonText: 'my button text',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, props);
+ });
+
+ it('sets the primary button text', () => {
+ const primaryButton = vm.$el.querySelector('.modal-footer button:last-of-type');
+ expect(primaryButton.innerHTML.trim()).toBe(props.footerPrimaryButtonText);
+ });
+ });
+ });
+
+ it('works with data-toggle="modal"', (done) => {
+ setFixtures(`
+ <button id="modal-button" data-toggle="modal" data-target="#my-modal"></button>
+ <div id="modal-container"></div>
+ `);
+
+ const modalContainer = document.getElementById('modal-container');
+ const modalButton = document.getElementById('modal-button');
+ vm = mountComponent(modalComponent, {
+ id: 'my-modal',
+ }, modalContainer);
+ $(vm.$el).on('shown.bs.modal', () => done());
+
+ modalButton.click();
+ });
+
+ describe('methods', () => {
+ const dummyEvent = 'not really an event';
+
+ beforeEach(() => {
+ vm = mountComponent(modalComponent, { });
+ spyOn(vm, '$emit');
+ });
+
+ describe('emitCancel', () => {
+ it('emits a cancel event', () => {
+ vm.emitCancel(dummyEvent);
+
+ expect(vm.$emit).toHaveBeenCalledWith('cancel', dummyEvent);
+ });
+ });
+
+ describe('emitSubmit', () => {
+ it('emits a submit event', () => {
+ vm.emitSubmit(dummyEvent);
+
+ expect(vm.$emit).toHaveBeenCalledWith('submit', dummyEvent);
+ });
+ });
+ });
+
+ describe('slots', () => {
+ const slotContent = 'this should go into the slot';
+ const modalWithSlot = (slotName) => {
+ let template;
+ if (slotName) {
+ template = `
+ <gl-modal>
+ <template slot="${slotName}">${slotContent}</template>
+ </gl-modal>
+ `;
+ } else {
+ template = `<gl-modal>${slotContent}</gl-modal>`;
+ }
+
+ return Vue.extend({
+ components: {
+ GlModal,
+ },
+ template,
+ });
+ };
+
+ describe('default slot', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalWithSlot());
+ });
+
+ it('sets the modal body', () => {
+ const modalBody = vm.$el.querySelector('.modal-body');
+ expect(modalBody.innerHTML).toBe(slotContent);
+ });
+ });
+
+ describe('header slot', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalWithSlot('header'));
+ });
+
+ it('sets the modal header', () => {
+ const modalHeader = vm.$el.querySelector('.modal-header');
+ expect(modalHeader.innerHTML).toBe(slotContent);
+ });
+ });
+
+ describe('title slot', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalWithSlot('title'));
+ });
+
+ it('sets the modal title', () => {
+ const modalTitle = vm.$el.querySelector('.modal-title');
+ expect(modalTitle.innerHTML).toBe(slotContent);
+ });
+ });
+
+ describe('footer slot', () => {
+ beforeEach(() => {
+ vm = mountComponent(modalWithSlot('footer'));
+ });
+
+ it('sets the modal footer', () => {
+ const modalFooter = vm.$el.querySelector('.modal-footer');
+ expect(modalFooter.innerHTML).toBe(slotContent);
+ });
+ });
+ });
+});
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 b378a0bd896..65499a2d730 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import headerCi from '~/vue_shared/components/header_ci_component.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Header CI Component', () => {
let HeaderCi;
diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js
index a22b6bd3a67..68d57ebc8f0 100644
--- a/spec/javascripts/vue_shared/components/icon_spec.js
+++ b/spec/javascripts/vue_shared/components/icon_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import Icon from '~/vue_shared/components/icon.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Sprite Icon Component', function () {
describe('Initialization', function () {
diff --git a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
index 24484796bf1..e6ed77dbb52 100644
--- a/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
+++ b/spec/javascripts/vue_shared/components/issue/issue_warning_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import issueWarning from '~/vue_shared/components/issue/issue_warning.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const IssueWarning = Vue.extend(issueWarning);
diff --git a/spec/javascripts/vue_shared/components/loading_button_spec.js b/spec/javascripts/vue_shared/components/loading_button_spec.js
index 49bf8ee6f7c..51c19cd4080 100644
--- a/spec/javascripts/vue_shared/components/loading_button_spec.js
+++ b/spec/javascripts/vue_shared/components/loading_button_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import loadingButton from '~/vue_shared/components/loading_button.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const LABEL = 'Hello';
diff --git a/spec/javascripts/vue_shared/components/modal_spec.js b/spec/javascripts/vue_shared/components/modal_spec.js
index a5f9c75be4e..8412df74f98 100644
--- a/spec/javascripts/vue_shared/components/modal_spec.js
+++ b/spec/javascripts/vue_shared/components/modal_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import modal from '~/vue_shared/components/modal.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const modalComponent = Vue.extend(modal);
diff --git a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
index 78e7d747b92..09fda95d7d3 100644
--- a/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
+++ b/spec/javascripts/vue_shared/components/navigation_tabs_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import navigationTabs from '~/vue_shared/components/navigation_tabs.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('navigation tabs component', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js b/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
index 7b8e6c330c2..262571efcb8 100644
--- a/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
+++ b/spec/javascripts/vue_shared/components/notes/placeholder_system_note_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import placeholderSystemNote from '~/vue_shared/components/notes/placeholder_system_note.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('placeholder system note component', () => {
let PlaceholderSystemNote;
diff --git a/spec/javascripts/vue_shared/components/panel_resizer_spec.js b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
index 70ce3dffaba..8efcb54659d 100644
--- a/spec/javascripts/vue_shared/components/panel_resizer_spec.js
+++ b/spec/javascripts/vue_shared/components/panel_resizer_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import panelResizer from '~/vue_shared/components/panel_resizer.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Panel Resizer component', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/pikaday_spec.js b/spec/javascripts/vue_shared/components/pikaday_spec.js
index 47af9534737..b349e2a2a81 100644
--- a/spec/javascripts/vue_shared/components/pikaday_spec.js
+++ b/spec/javascripts/vue_shared/components/pikaday_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import datePicker from '~/vue_shared/components/pikaday.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('datePicker', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
index cce53193870..8c296af6652 100644
--- a/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_calendar_icon_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import collapsedCalendarIcon from '~/vue_shared/components/sidebar/collapsed_calendar_icon.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('collapsedCalendarIcon', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
index 2de108da2ac..9d60f9c758f 100644
--- a/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/collapsed_grouped_date_picker_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import collapsedGroupedDatePicker from '~/vue_shared/components/sidebar/collapsed_grouped_date_picker.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('collapsedGroupedDatePicker', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
index 926e11b4d30..8840a5a9dbf 100644
--- a/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/date_picker_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import sidebarDatePicker from '~/vue_shared/components/sidebar/date_picker.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('sidebarDatePicker', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js b/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js
index 752a9e89d50..c911a129173 100644
--- a/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js
+++ b/spec/javascripts/vue_shared/components/sidebar/toggle_sidebar_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import toggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('toggleSidebar', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js b/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
index a5db0b2c59e..bbd50863069 100644
--- a/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
+++ b/spec/javascripts/vue_shared/components/skeleton_loading_container_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import skeletonLoadingContainer from '~/vue_shared/components/skeleton_loading_container.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Skeleton loading container', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
index 6940b04573e..de3bf667fb3 100644
--- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
+++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js
@@ -2,7 +2,7 @@ import Vue from 'vue';
import stackedProgressBarComponent from '~/vue_shared/components/stacked_progress_bar.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const createComponent = (config) => {
const Component = Vue.extend(stackedProgressBarComponent);
diff --git a/spec/javascripts/vue_shared/components/toggle_button_spec.js b/spec/javascripts/vue_shared/components/toggle_button_spec.js
index 859995d33fa..71952cc39e0 100644
--- a/spec/javascripts/vue_shared/components/toggle_button_spec.js
+++ b/spec/javascripts/vue_shared/components/toggle_button_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import toggleButton from '~/vue_shared/components/toggle_button.vue';
-import mountComponent from '../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Toggle Button', () => {
let vm;
diff --git a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
index aa93134f2dd..446f025c127 100644
--- a/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
+++ b/spec/javascripts/vue_shared/components/user_avatar/user_avatar_image_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import { placeholderImage } from '~/lazy_loader';
import userAvatarImage from '~/vue_shared/components/user_avatar/user_avatar_image.vue';
-import mountComponent from '../../../helpers/vue_mount_component_helper';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
const DEFAULT_PROPS = {
size: 99,
diff --git a/spec/lib/backup/repository_spec.rb b/spec/lib/backup/repository_spec.rb
index 6ee3d531d6e..f7b1a61f4f8 100644
--- a/spec/lib/backup/repository_spec.rb
+++ b/spec/lib/backup/repository_spec.rb
@@ -33,10 +33,22 @@ describe Backup::Repository do
allow(Gitlab::Popen).to receive(:popen).and_return(['error', 1])
end
- it 'shows the appropriate error' do
- described_class.new.restore
+ context 'hashed storage' do
+ it 'shows the appropriate error' do
+ described_class.new.restore
- expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error")
+ expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} (#{project.disk_path}) - error")
+ end
+ end
+
+ context 'legacy storage' do
+ let!(:project) { create(:project, :legacy_storage) }
+
+ it 'shows the appropriate error' do
+ described_class.new.restore
+
+ expect(progress).to have_received(:puts).with("Ignoring error on #{project.full_path} - error")
+ end
end
end
end
diff --git a/spec/lib/banzai/commit_renderer_spec.rb b/spec/lib/banzai/commit_renderer_spec.rb
index 84adaebdcbe..e7ebb2a332f 100644
--- a/spec/lib/banzai/commit_renderer_spec.rb
+++ b/spec/lib/banzai/commit_renderer_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Banzai::CommitRenderer do
describe '.render' do
it 'renders a commit description and title' do
- user = double(:user)
+ user = build(:user)
project = create(:project, :repository)
expect(Banzai::ObjectRenderer).to receive(:new).with(project, user).and_call_original
diff --git a/spec/lib/banzai/filter/html_entity_filter_spec.rb b/spec/lib/banzai/filter/html_entity_filter_spec.rb
index 91e18d876d5..1d98fc0d5db 100644
--- a/spec/lib/banzai/filter/html_entity_filter_spec.rb
+++ b/spec/lib/banzai/filter/html_entity_filter_spec.rb
@@ -3,17 +3,12 @@ require 'spec_helper'
describe Banzai::Filter::HtmlEntityFilter do
include FilterSpecHelper
- let(:unescaped) { 'foo <strike attr="foo">&&&</strike>' }
- let(:escaped) { 'foo &lt;strike attr=&quot;foo&quot;&gt;&amp;&amp;&amp;&lt;/strike&gt;' }
+ let(:unescaped) { 'foo <strike attr="foo">&&amp;&</strike>' }
+ let(:escaped) { 'foo &lt;strike attr=&quot;foo&quot;&gt;&amp;&amp;amp;&amp;&lt;/strike&gt;' }
it 'converts common entities to their HTML-escaped equivalents' do
output = filter(unescaped)
expect(output).to eq(escaped)
end
-
- it 'does not double-escape' do
- escaped = ERB::Util.html_escape("Merge branch 'blabla' into 'master'")
- expect(filter(escaped)).to eq(escaped)
- end
end
diff --git a/spec/lib/banzai/filter/issuable_state_filter_spec.rb b/spec/lib/banzai/filter/issuable_state_filter_spec.rb
index cacb33d3372..17347768a49 100644
--- a/spec/lib/banzai/filter/issuable_state_filter_spec.rb
+++ b/spec/lib/banzai/filter/issuable_state_filter_spec.rb
@@ -77,6 +77,14 @@ describe Banzai::Filter::IssuableStateFilter do
expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)} (closed)")
end
+ it 'skips cross project references if the user cannot read cross project' do
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ link = create_link(closed_issue.to_reference(other_project), issue: closed_issue.id, reference_type: 'issue')
+ doc = filter(link, context.merge(project: other_project))
+
+ expect(doc.css('a').last.text).to eq("#{closed_issue.to_reference(other_project)}")
+ end
+
it 'does not append state when filter is not enabled' do
link = create_link('text', issue: closed_issue.id, reference_type: 'issue')
context = { current_user: user }
diff --git a/spec/lib/banzai/filter/redactor_filter_spec.rb b/spec/lib/banzai/filter/redactor_filter_spec.rb
index 5a7858e77f3..9a2e521fdcf 100644
--- a/spec/lib/banzai/filter/redactor_filter_spec.rb
+++ b/spec/lib/banzai/filter/redactor_filter_spec.rb
@@ -6,7 +6,7 @@ describe Banzai::Filter::RedactorFilter do
it 'ignores non-GFM links' do
html = %(See <a href="https://google.com/">Google</a>)
- doc = filter(html, current_user: double)
+ doc = filter(html, current_user: build(:user))
expect(doc.css('a').length).to eq 1
end
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index 9f2efa05a01..ef52c572898 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -3,35 +3,86 @@ require 'spec_helper'
describe Banzai::Filter::SyntaxHighlightFilter do
include FilterSpecHelper
+ shared_examples "XSS prevention" do |lang|
+ it "escapes HTML tags" do
+ # This is how a script tag inside a code block is presented to this filter
+ # after Markdown rendering.
+ result = filter(%{<pre lang="#{lang}"><code>&lt;script&gt;alert(1)&lt;/script&gt;</code></pre>})
+
+ expect(result.to_html).not_to include("<script>alert(1)</script>")
+ expect(result.to_html).to include("alert(1)")
+ end
+ end
+
context "when no language is specified" do
it "highlights as plaintext" do
result = filter('<pre><code>def fun end</code></pre>')
+
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">def fun end</span></code></pre>')
end
+
+ include_examples "XSS prevention", ""
end
context "when a valid language is specified" do
it "highlights as that language" do
result = filter('<pre><code lang="ruby">def fun end</code></pre>')
+
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight ruby" lang="ruby" v-pre="true"><code><span id="LC1" class="line" lang="ruby"><span class="k">def</span> <span class="nf">fun</span> <span class="k">end</span></span></code></pre>')
end
+
+ include_examples "XSS prevention", "ruby"
end
context "when an invalid language is specified" do
it "highlights as plaintext" do
result = filter('<pre><code lang="gnuplot">This is a test</code></pre>')
+
expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight plaintext" lang="plaintext" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">This is a test</span></code></pre>')
end
+
+ include_examples "XSS prevention", "gnuplot"
end
- context "when Rouge formatting fails" do
+ context "languages that should be passed through" do
+ %w(math mermaid plantuml).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>})
+
+ expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight #{lang}" lang="#{lang}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>})
+ end
+
+ include_examples "XSS prevention", lang
+ end
+ end
+ end
+
+ context "when Rouge lexing fails" do
before do
- allow_any_instance_of(Rouge::Formatter).to receive(:format).and_raise(StandardError)
+ allow_any_instance_of(Rouge::Lexers::Ruby).to receive(:stream_tokens).and_raise(StandardError)
end
it "highlights as plaintext" do
result = filter('<pre><code lang="ruby">This is a test</code></pre>')
- expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code>This is a test</code></pre>')
+
+ expect(result.to_html).to eq('<pre class="code highlight js-syntax-highlight" lang="" v-pre="true"><code><span id="LC1" class="line" lang="">This is a test</span></code></pre>')
+ end
+
+ include_examples "XSS prevention", "ruby"
+ end
+
+ context "when Rouge lexing fails after a retry" do
+ before do
+ allow_any_instance_of(Rouge::Lexers::PlainText).to receive(:stream_tokens).and_raise(StandardError)
+ end
+
+ it "does not add highlighting classes" do
+ result = filter('<pre><code>This is a test</code></pre>')
+
+ expect(result.to_html).to eq('<pre><code>This is a test</code></pre>')
end
+
+ include_examples "XSS prevention", "ruby"
end
end
diff --git a/spec/lib/banzai/redactor_spec.rb b/spec/lib/banzai/redactor_spec.rb
index 2424c3fdc66..1fa89137972 100644
--- a/spec/lib/banzai/redactor_spec.rb
+++ b/spec/lib/banzai/redactor_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Banzai::Redactor do
- let(:user) { build(:user) }
+ let(:user) { create(:user) }
let(:project) { build(:project) }
let(:redactor) { described_class.new(project, user) }
@@ -88,6 +88,55 @@ describe Banzai::Redactor do
end
end
+ context 'when the user cannot read cross project' do
+ include ActionView::Helpers::UrlHelper
+ let(:project) { create(:project) }
+ let(:other_project) { create(:project, :public) }
+
+ def create_link(issuable)
+ type = issuable.class.name.underscore.downcase
+ link_to(issuable.to_reference, '',
+ class: 'gfm has-tooltip',
+ title: issuable.title,
+ data: {
+ reference_type: type,
+ "#{type}": issuable.id
+ })
+ end
+
+ before do
+ project.add_developer(user)
+
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global) { false }
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ it 'skips links to issues within the same project' do
+ issue = create(:issue, project: project)
+ link = create_link(issue)
+ doc = Nokogiri::HTML.fragment(link)
+
+ redactor.redact([doc])
+ result = doc.css('a').last
+
+ expect(result['class']).to include('has-tooltip')
+ expect(result['title']).to eq(issue.title)
+ end
+
+ it 'removes info from a cross project reference' do
+ issue = create(:issue, project: other_project)
+ link = create_link(issue)
+ doc = Nokogiri::HTML.fragment(link)
+
+ redactor.redact([doc])
+ result = doc.css('a').last
+
+ expect(result['class']).not_to include('has-tooltip')
+ expect(result['title']).to be_empty
+ end
+ end
+
describe '#redact_nodes' do
it 'redacts an Array of nodes' do
doc = Nokogiri::HTML.fragment('<a href="foo">foo</a>')
diff --git a/spec/lib/banzai/reference_parser/issue_parser_spec.rb b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
index 4cef3bdb24b..0a63567ee40 100644
--- a/spec/lib/banzai/reference_parser/issue_parser_spec.rb
+++ b/spec/lib/banzai/reference_parser/issue_parser_spec.rb
@@ -19,19 +19,58 @@ describe Banzai::ReferenceParser::IssueParser do
it 'returns the nodes when the user can read the issue' do
expect(Ability).to receive(:issues_readable_by_user)
- .with([issue], user)
- .and_return([issue])
+ .with([issue], user)
+ .and_return([issue])
expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
end
it 'returns an empty Array when the user can not read the issue' do
expect(Ability).to receive(:issues_readable_by_user)
- .with([issue], user)
- .and_return([])
+ .with([issue], user)
+ .and_return([])
expect(subject.nodes_visible_to_user(user, [link])).to eq([])
end
+
+ context 'when the user cannot read cross project' do
+ let(:issue) { create(:issue) }
+
+ before do
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global) { false }
+ end
+
+ it 'returns the nodes when the user can read the issue' do
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_issue_iid, issue)
+ .and_return(true)
+
+ expect(subject.nodes_visible_to_user(user, [link])).to eq([link])
+ end
+
+ it 'returns an empty Array when the user can not read the issue' do
+ expect(Ability).to receive(:allowed?)
+ .with(user, :read_issue_iid, issue)
+ .and_return(false)
+
+ expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+ end
+
+ context 'when the issue is not cross project' do
+ let(:issue) { create(:issue, project: project) }
+
+ it 'does not check `can_read_reference` if the issue is not cross project' do
+ expect(Ability).to receive(:issues_readable_by_user)
+ .with([issue], user)
+ .and_return([])
+
+ expect(subject).not_to receive(:can_read_reference?).with(user, issue)
+
+ expect(subject.nodes_visible_to_user(user, [link])).to eq([])
+ end
+ end
+ end
end
context 'when the link does not have a data-issue attribute' do
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index f668f78c2b8..2a0e19ae796 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -95,6 +95,14 @@ module Gitlab
expect(render(input, context)).to include('<p><code data-math-style="inline" class="code math js-render-math">2+2</code> is 4</p>')
end
end
+
+ context 'outfilesuffix' do
+ it 'defaults to adoc' do
+ output = render("Inter-document reference <<README.adoc#>>", context)
+
+ expect(output).to include("a href=\"README.adoc\"")
+ end
+ end
end
def render(*args)
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb
new file mode 100644
index 00000000000..c76adcbe2f5
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_dependencies/untracked_file_spec.rb
@@ -0,0 +1,262 @@
+require 'spec_helper'
+
+# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work.
+describe Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::UntrackedFile, :migration, schema: 20180208183958 do
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
+
+ let!(:appearances) { table(:appearances) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:routes) { table(:routes) }
+ let!(:uploads) { table(:uploads) }
+
+ before(:all) do
+ ensure_temporary_tracking_table_exists
+ end
+
+ describe '#upload_path' do
+ def assert_upload_path(file_path, expected_upload_path)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.upload_path).to eq(expected_upload_path)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/appearance/logo/1/some_logo.jpg', 'uploads/-/system/appearance/logo/1/some_logo.jpg')
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/appearance/header_logo/1/some_logo.jpg', 'uploads/-/system/appearance/header_logo/1/some_logo.jpg')
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/note/attachment/1234/some_attachment.pdf', 'uploads/-/system/note/attachment/1234/some_attachment.pdf')
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/user/avatar/1234/avatar.jpg', 'uploads/-/system/user/avatar/1234/avatar.jpg')
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/group/avatar/1234/avatar.jpg', 'uploads/-/system/group/avatar/1234/avatar.jpg')
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns the file path relative to the CarrierWave root' do
+ assert_upload_path('/-/system/project/avatar/1234/avatar.jpg', 'uploads/-/system/project/avatar/1234/avatar.jpg')
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns the file path relative to the project directory in uploads' do
+ project = create_project
+ random_hex = SecureRandom.hex
+
+ assert_upload_path("/#{get_full_path(project)}/#{random_hex}/Some file.jpg", "#{random_hex}/Some file.jpg")
+ end
+ end
+ end
+
+ describe '#uploader' do
+ def assert_uploader(file_path, expected_uploader)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.uploader).to eq(expected_uploader)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns AttachmentUploader as a string' do
+ assert_uploader('/-/system/appearance/logo/1/some_logo.jpg', 'AttachmentUploader')
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns AttachmentUploader as a string' do
+ assert_uploader('/-/system/appearance/header_logo/1/some_logo.jpg', 'AttachmentUploader')
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns AttachmentUploader as a string' do
+ assert_uploader('/-/system/note/attachment/1234/some_attachment.pdf', 'AttachmentUploader')
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns AvatarUploader as a string' do
+ assert_uploader('/-/system/user/avatar/1234/avatar.jpg', 'AvatarUploader')
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns AvatarUploader as a string' do
+ assert_uploader('/-/system/group/avatar/1234/avatar.jpg', 'AvatarUploader')
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns AvatarUploader as a string' do
+ assert_uploader('/-/system/project/avatar/1234/avatar.jpg', 'AvatarUploader')
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns FileUploader as a string' do
+ project = create_project
+
+ assert_uploader("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", 'FileUploader')
+ end
+ end
+ end
+
+ describe '#model_type' do
+ def assert_model_type(file_path, expected_model_type)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.model_type).to eq(expected_model_type)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns Appearance as a string' do
+ assert_model_type('/-/system/appearance/logo/1/some_logo.jpg', 'Appearance')
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns Appearance as a string' do
+ assert_model_type('/-/system/appearance/header_logo/1/some_logo.jpg', 'Appearance')
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns Note as a string' do
+ assert_model_type('/-/system/note/attachment/1234/some_attachment.pdf', 'Note')
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns User as a string' do
+ assert_model_type('/-/system/user/avatar/1234/avatar.jpg', 'User')
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns Namespace as a string' do
+ assert_model_type('/-/system/group/avatar/1234/avatar.jpg', 'Namespace')
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns Project as a string' do
+ assert_model_type('/-/system/project/avatar/1234/avatar.jpg', 'Project')
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns Project as a string' do
+ project = create_project
+
+ assert_model_type("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", 'Project')
+ end
+ end
+ end
+
+ describe '#model_id' do
+ def assert_model_id(file_path, expected_model_id)
+ untracked_file = create_untracked_file(file_path)
+
+ expect(untracked_file.model_id).to eq(expected_model_id)
+ end
+
+ context 'for an appearance logo file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/appearance/logo/1/some_logo.jpg', 1)
+ end
+ end
+
+ context 'for an appearance header_logo file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/appearance/header_logo/1/some_logo.jpg', 1)
+ end
+ end
+
+ context 'for a pre-Markdown Note attachment file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/note/attachment/1234/some_attachment.pdf', 1234)
+ end
+ end
+
+ context 'for a user avatar file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/user/avatar/1234/avatar.jpg', 1234)
+ end
+ end
+
+ context 'for a group avatar file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/group/avatar/1234/avatar.jpg', 1234)
+ end
+ end
+
+ context 'for a project avatar file path' do
+ it 'returns the ID as a string' do
+ assert_model_id('/-/system/project/avatar/1234/avatar.jpg', 1234)
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ it 'returns the ID as a string' do
+ project = create_project
+
+ assert_model_id("/#{get_full_path(project)}/#{SecureRandom.hex}/Some file.jpg", project.id)
+ end
+ end
+ end
+
+ describe '#file_size' do
+ context 'for an appearance logo file path' do
+ let(:appearance) { create_or_update_appearance(logo: true) }
+ let(:untracked_file) { described_class.create!(path: get_uploads(appearance, 'Appearance').first.path) }
+
+ it 'returns the file size' do
+ expect(untracked_file.file_size).to eq(1062)
+ end
+ end
+
+ context 'for a project avatar file path' do
+ let(:project) { create_project(avatar: true) }
+ let(:untracked_file) { described_class.create!(path: get_uploads(project, 'Project').first.path) }
+
+ it 'returns the file size' do
+ expect(untracked_file.file_size).to eq(1062)
+ end
+ end
+
+ context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
+ let(:project) { create_project }
+ let(:untracked_file) { create_untracked_file("/#{get_full_path(project)}/#{get_uploads(project, 'Project').first.path}") }
+
+ before do
+ add_markdown_attachment(project)
+ end
+
+ it 'returns the file size' do
+ expect(untracked_file.file_size).to eq(1062)
+ end
+ end
+ end
+
+ def create_untracked_file(path_relative_to_upload_dir)
+ described_class.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}#{path_relative_to_upload_dir}")
+ end
+end
diff --git a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
index be45c00dfe6..0d2074eed22 100644
--- a/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/populate_untracked_uploads_spec.rb
@@ -1,59 +1,60 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
- include TrackUntrackedUploadsHelpers
+# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work.
+describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq, :migration, schema: 20180208183958 do
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
subject { described_class.new }
- let!(:untracked_files_for_uploads) { described_class::UntrackedFile }
- let!(:uploads) { described_class::Upload }
+ let!(:appearances) { table(:appearances) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:notes) { table(:notes) }
+ let!(:projects) { table(:projects) }
+ let!(:routes) { table(:routes) }
+ let!(:untracked_files_for_uploads) { table(:untracked_files_for_uploads) }
+ let!(:uploads) { table(:uploads) }
+ let!(:users) { table(:users) }
before do
- DatabaseCleaner.clean
- drop_temp_table_if_exists
ensure_temporary_tracking_table_exists
uploads.delete_all
end
- after(:all) do
- drop_temp_table_if_exists
- end
-
context 'with untracked files and tracked files in untracked_files_for_uploads' do
- let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) }
- let!(:user1) { create(:user, :with_avatar) }
- let!(:user2) { create(:user, :with_avatar) }
- let!(:project1) { create(:project, :with_avatar) }
- let!(:project2) { create(:project, :with_avatar) }
+ let!(:appearance) { create_or_update_appearance(logo: true, header_logo: true) }
+ let!(:user1) { create_user(avatar: true) }
+ let!(:user2) { create_user(avatar: true) }
+ let!(:project1) { create_project(avatar: true) }
+ let!(:project2) { create_project(avatar: true) }
before do
- UploadService.new(project1, uploaded_file, FileUploader).execute # Markdown upload
- UploadService.new(project2, uploaded_file, FileUploader).execute # Markdown upload
+ add_markdown_attachment(project1)
+ add_markdown_attachment(project2)
# File records created by PrepareUntrackedUploads
- untracked_files_for_uploads.create!(path: appearance.uploads.first.path)
- untracked_files_for_uploads.create!(path: appearance.uploads.last.path)
- untracked_files_for_uploads.create!(path: user1.uploads.first.path)
- untracked_files_for_uploads.create!(path: user2.uploads.first.path)
- untracked_files_for_uploads.create!(path: project1.uploads.first.path)
- untracked_files_for_uploads.create!(path: project2.uploads.first.path)
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project1.full_path}/#{project1.uploads.last.path}")
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/#{project2.uploads.last.path}")
+ untracked_files_for_uploads.create!(path: get_uploads(appearance, 'Appearance').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(appearance, 'Appearance').last.path)
+ untracked_files_for_uploads.create!(path: get_uploads(user1, 'User').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(user2, 'User').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(project1, 'Project').first.path)
+ untracked_files_for_uploads.create!(path: get_uploads(project2, 'Project').first.path)
+ untracked_files_for_uploads.create!(path: "#{legacy_project_uploads_dir(project1).sub("#{MigrationsHelpers::TrackUntrackedUploadsHelpers::PUBLIC_DIR}/", '')}/#{get_uploads(project1, 'Project').last.path}")
+ untracked_files_for_uploads.create!(path: "#{legacy_project_uploads_dir(project2).sub("#{MigrationsHelpers::TrackUntrackedUploadsHelpers::PUBLIC_DIR}/", '')}/#{get_uploads(project2, 'Project').last.path}")
# Untrack 4 files
- user2.uploads.delete_all
- project2.uploads.delete_all # 2 files: avatar and a Markdown upload
- appearance.uploads.where("path like '%header_logo%'").delete_all
+ get_uploads(user2, 'User').delete_all
+ get_uploads(project2, 'Project').delete_all # 2 files: avatar and a Markdown upload
+ get_uploads(appearance, 'Appearance').where("path like '%header_logo%'").delete_all
end
it 'adds untracked files to the uploads table' do
expect do
- subject.perform(1, untracked_files_for_uploads.last.id)
+ subject.perform(1, untracked_files_for_uploads.reorder(:id).last.id)
end.to change { uploads.count }.from(4).to(8)
- expect(user2.uploads.count).to eq(1)
- expect(project2.uploads.count).to eq(2)
- expect(appearance.uploads.count).to eq(2)
+ expect(get_uploads(user2, 'User').count).to eq(1)
+ expect(get_uploads(project2, 'Project').count).to eq(2)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(2)
end
it 'deletes rows after processing them' do
@@ -67,9 +68,9 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
it 'does not create duplicate uploads of already tracked files' do
subject.perform(1, untracked_files_for_uploads.last.id)
- expect(user1.uploads.count).to eq(1)
- expect(project1.uploads.count).to eq(2)
- expect(appearance.uploads.count).to eq(2)
+ expect(get_uploads(user1, 'User').count).to eq(1)
+ expect(get_uploads(project1, 'Project').count).to eq(2)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(2)
end
it 'uses the start and end batch ids [only 1st half]' do
@@ -81,11 +82,11 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
subject.perform(start_id, end_id)
end.to change { uploads.count }.from(4).to(6)
- expect(user1.uploads.count).to eq(1)
- expect(user2.uploads.count).to eq(1)
- expect(appearance.uploads.count).to eq(2)
- expect(project1.uploads.count).to eq(2)
- expect(project2.uploads.count).to eq(0)
+ expect(get_uploads(user1, 'User').count).to eq(1)
+ expect(get_uploads(user2, 'User').count).to eq(1)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(2)
+ expect(get_uploads(project1, 'Project').count).to eq(2)
+ expect(get_uploads(project2, 'Project').count).to eq(0)
# Only 4 have been either confirmed or added to uploads
expect(untracked_files_for_uploads.count).to eq(4)
@@ -100,11 +101,11 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
subject.perform(start_id, end_id)
end.to change { uploads.count }.from(4).to(6)
- expect(user1.uploads.count).to eq(1)
- expect(user2.uploads.count).to eq(0)
- expect(appearance.uploads.count).to eq(1)
- expect(project1.uploads.count).to eq(2)
- expect(project2.uploads.count).to eq(2)
+ expect(get_uploads(user1, 'User').count).to eq(1)
+ expect(get_uploads(user2, 'User').count).to eq(0)
+ expect(get_uploads(appearance, 'Appearance').count).to eq(1)
+ expect(get_uploads(project1, 'Project').count).to eq(2)
+ expect(get_uploads(project2, 'Project').count).to eq(2)
# Only 4 have been either confirmed or added to uploads
expect(untracked_files_for_uploads.count).to eq(4)
@@ -117,13 +118,13 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
end
it 'drops the temporary tracking table after processing the batch, if there are no untracked rows left' do
- subject.perform(1, untracked_files_for_uploads.last.id)
+ expect(subject).to receive(:drop_temp_table_if_finished)
- expect(ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads)).to be_falsey
+ subject.perform(1, untracked_files_for_uploads.last.id)
end
it 'does not block a whole batch because of one bad path' do
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/._7d37bf4c747916390e596744117d5d1a")
+ untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(project2)}/._7d37bf4c747916390e596744117d5d1a")
expect(untracked_files_for_uploads.count).to eq(9)
expect(uploads.count).to eq(4)
@@ -134,7 +135,7 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
end
it 'an unparseable path is shown in error output' do
- bad_path = "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{project2.full_path}/._7d37bf4c747916390e596744117d5d1a"
+ bad_path = "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(project2)}/._7d37bf4c747916390e596744117d5d1a"
untracked_files_for_uploads.create!(path: bad_path)
expect(Rails.logger).to receive(:error).with(/Error parsing path "#{bad_path}":/)
@@ -153,367 +154,100 @@ describe Gitlab::BackgroundMigration::PopulateUntrackedUploads, :sidekiq do
describe 'upload outcomes for each path pattern' do
shared_examples_for 'non_markdown_file' do
- let!(:expected_upload_attrs) { model.uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') }
+ let!(:expected_upload_attrs) { model_uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum') }
let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) }
before do
- model.uploads.delete_all
+ model_uploads.delete_all
end
it 'creates an Upload record' do
expect do
subject.perform(1, untracked_files_for_uploads.last.id)
- end.to change { model.reload.uploads.count }.from(0).to(1)
+ end.to change { model_uploads.count }.from(0).to(1)
- expect(model.uploads.first.attributes).to include(expected_upload_attrs)
+ expect(model_uploads.first.attributes).to include(expected_upload_attrs)
end
end
context 'for an appearance logo file path' do
- let(:model) { create_or_update_appearance(logo: uploaded_file) }
+ let(:model) { create_or_update_appearance(logo: true) }
+ let(:model_uploads) { get_uploads(model, 'Appearance') }
it_behaves_like 'non_markdown_file'
end
context 'for an appearance header_logo file path' do
- let(:model) { create_or_update_appearance(header_logo: uploaded_file) }
+ let(:model) { create_or_update_appearance(header_logo: true) }
+ let(:model_uploads) { get_uploads(model, 'Appearance') }
it_behaves_like 'non_markdown_file'
end
context 'for a pre-Markdown Note attachment file path' do
- let(:model) { create(:note, :with_attachment) }
- let!(:expected_upload_attrs) { Upload.where(model_type: 'Note', model_id: model.id).first.attributes.slice('path', 'uploader', 'size', 'checksum') }
+ let(:model) { create_note(attachment: true) }
+ let!(:expected_upload_attrs) { get_uploads(model, 'Note').first.attributes.slice('path', 'uploader', 'size', 'checksum') }
let!(:untracked_file) { untracked_files_for_uploads.create!(path: expected_upload_attrs['path']) }
before do
- Upload.where(model_type: 'Note', model_id: model.id).delete_all
+ get_uploads(model, 'Note').delete_all
end
# Can't use the shared example because Note doesn't have an `uploads` association
it 'creates an Upload record' do
expect do
subject.perform(1, untracked_files_for_uploads.last.id)
- end.to change { Upload.where(model_type: 'Note', model_id: model.id).count }.from(0).to(1)
+ end.to change { get_uploads(model, 'Note').count }.from(0).to(1)
- expect(Upload.where(model_type: 'Note', model_id: model.id).first.attributes).to include(expected_upload_attrs)
+ expect(get_uploads(model, 'Note').first.attributes).to include(expected_upload_attrs)
end
end
context 'for a user avatar file path' do
- let(:model) { create(:user, :with_avatar) }
+ let(:model) { create_user(avatar: true) }
+ let(:model_uploads) { get_uploads(model, 'User') }
it_behaves_like 'non_markdown_file'
end
context 'for a group avatar file path' do
- let(:model) { create(:group, :with_avatar) }
+ let(:model) { create_group(avatar: true) }
+ let(:model_uploads) { get_uploads(model, 'Namespace') }
it_behaves_like 'non_markdown_file'
end
context 'for a project avatar file path' do
- let(:model) { create(:project, :with_avatar) }
+ let(:model) { create_project(avatar: true) }
+ let(:model_uploads) { get_uploads(model, 'Project') }
it_behaves_like 'non_markdown_file'
end
context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- let(:model) { create(:project) }
+ let(:model) { create_project }
before do
# Upload the file
- UploadService.new(model, uploaded_file, FileUploader).execute
+ add_markdown_attachment(model)
# Create the untracked_files_for_uploads record
- untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{model.full_path}/#{model.uploads.first.path}")
+ untracked_files_for_uploads.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}/#{get_full_path(model)}/#{get_uploads(model, 'Project').first.path}")
# Save the expected upload attributes
- @expected_upload_attrs = model.reload.uploads.first.attributes.slice('path', 'uploader', 'size', 'checksum')
+ @expected_upload_attrs = get_uploads(model, 'Project').first.attributes.slice('path', 'uploader', 'size', 'checksum')
# Untrack the file
- model.reload.uploads.delete_all
+ get_uploads(model, 'Project').delete_all
end
it 'creates an Upload record' do
expect do
subject.perform(1, untracked_files_for_uploads.last.id)
- end.to change { model.reload.uploads.count }.from(0).to(1)
-
- expect(model.uploads.first.attributes).to include(@expected_upload_attrs)
- end
- end
- end
-end
-
-describe Gitlab::BackgroundMigration::PopulateUntrackedUploads::UntrackedFile do
- include TrackUntrackedUploadsHelpers
-
- let(:upload_class) { Gitlab::BackgroundMigration::PopulateUntrackedUploads::Upload }
-
- before(:all) do
- ensure_temporary_tracking_table_exists
- end
-
- after(:all) do
- drop_temp_table_if_exists
- end
-
- describe '#upload_path' do
- def assert_upload_path(file_path, expected_upload_path)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.upload_path).to eq(expected_upload_path)
- end
-
- context 'for an appearance logo file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/appearance/logo/1/some_logo.jpg', 'uploads/-/system/appearance/logo/1/some_logo.jpg')
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/appearance/header_logo/1/some_logo.jpg', 'uploads/-/system/appearance/header_logo/1/some_logo.jpg')
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/note/attachment/1234/some_attachment.pdf', 'uploads/-/system/note/attachment/1234/some_attachment.pdf')
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/user/avatar/1234/avatar.jpg', 'uploads/-/system/user/avatar/1234/avatar.jpg')
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/group/avatar/1234/avatar.jpg', 'uploads/-/system/group/avatar/1234/avatar.jpg')
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns the file path relative to the CarrierWave root' do
- assert_upload_path('/-/system/project/avatar/1234/avatar.jpg', 'uploads/-/system/project/avatar/1234/avatar.jpg')
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns the file path relative to the project directory in uploads' do
- project = create(:project)
- random_hex = SecureRandom.hex
-
- assert_upload_path("/#{project.full_path}/#{random_hex}/Some file.jpg", "#{random_hex}/Some file.jpg")
- end
- end
- end
-
- describe '#uploader' do
- def assert_uploader(file_path, expected_uploader)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.uploader).to eq(expected_uploader)
- end
-
- context 'for an appearance logo file path' do
- it 'returns AttachmentUploader as a string' do
- assert_uploader('/-/system/appearance/logo/1/some_logo.jpg', 'AttachmentUploader')
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns AttachmentUploader as a string' do
- assert_uploader('/-/system/appearance/header_logo/1/some_logo.jpg', 'AttachmentUploader')
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns AttachmentUploader as a string' do
- assert_uploader('/-/system/note/attachment/1234/some_attachment.pdf', 'AttachmentUploader')
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns AvatarUploader as a string' do
- assert_uploader('/-/system/user/avatar/1234/avatar.jpg', 'AvatarUploader')
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns AvatarUploader as a string' do
- assert_uploader('/-/system/group/avatar/1234/avatar.jpg', 'AvatarUploader')
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns AvatarUploader as a string' do
- assert_uploader('/-/system/project/avatar/1234/avatar.jpg', 'AvatarUploader')
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns FileUploader as a string' do
- project = create(:project)
-
- assert_uploader("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", 'FileUploader')
- end
- end
- end
-
- describe '#model_type' do
- def assert_model_type(file_path, expected_model_type)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.model_type).to eq(expected_model_type)
- end
+ end.to change { get_uploads(model, 'Project').count }.from(0).to(1)
- context 'for an appearance logo file path' do
- it 'returns Appearance as a string' do
- assert_model_type('/-/system/appearance/logo/1/some_logo.jpg', 'Appearance')
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns Appearance as a string' do
- assert_model_type('/-/system/appearance/header_logo/1/some_logo.jpg', 'Appearance')
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns Note as a string' do
- assert_model_type('/-/system/note/attachment/1234/some_attachment.pdf', 'Note')
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns User as a string' do
- assert_model_type('/-/system/user/avatar/1234/avatar.jpg', 'User')
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns Namespace as a string' do
- assert_model_type('/-/system/group/avatar/1234/avatar.jpg', 'Namespace')
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns Project as a string' do
- assert_model_type('/-/system/project/avatar/1234/avatar.jpg', 'Project')
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns Project as a string' do
- project = create(:project)
-
- assert_model_type("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", 'Project')
- end
- end
- end
-
- describe '#model_id' do
- def assert_model_id(file_path, expected_model_id)
- untracked_file = create_untracked_file(file_path)
-
- expect(untracked_file.model_id).to eq(expected_model_id)
- end
-
- context 'for an appearance logo file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/appearance/logo/1/some_logo.jpg', 1)
- end
- end
-
- context 'for an appearance header_logo file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/appearance/header_logo/1/some_logo.jpg', 1)
- end
- end
-
- context 'for a pre-Markdown Note attachment file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/note/attachment/1234/some_attachment.pdf', 1234)
- end
- end
-
- context 'for a user avatar file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/user/avatar/1234/avatar.jpg', 1234)
- end
- end
-
- context 'for a group avatar file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/group/avatar/1234/avatar.jpg', 1234)
- end
- end
-
- context 'for a project avatar file path' do
- it 'returns the ID as a string' do
- assert_model_id('/-/system/project/avatar/1234/avatar.jpg', 1234)
+ expect(get_uploads(model, 'Project').first.attributes).to include(@expected_upload_attrs)
end
end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- it 'returns the ID as a string' do
- project = create(:project)
-
- assert_model_id("/#{project.full_path}/#{SecureRandom.hex}/Some file.jpg", project.id)
- end
- end
- end
-
- describe '#file_size' do
- context 'for an appearance logo file path' do
- let(:appearance) { create_or_update_appearance(logo: uploaded_file) }
- let(:untracked_file) { described_class.create!(path: appearance.uploads.first.path) }
-
- it 'returns the file size' do
- expect(untracked_file.file_size).to eq(35255)
- end
-
- it 'returns the same thing that CarrierWave would return' do
- expect(untracked_file.file_size).to eq(appearance.logo.size)
- end
- end
-
- context 'for a project avatar file path' do
- let(:project) { create(:project, avatar: uploaded_file) }
- let(:untracked_file) { described_class.create!(path: project.uploads.first.path) }
-
- it 'returns the file size' do
- expect(untracked_file.file_size).to eq(35255)
- end
-
- it 'returns the same thing that CarrierWave would return' do
- expect(untracked_file.file_size).to eq(project.avatar.size)
- end
- end
-
- context 'for a project Markdown attachment (notes, issues, MR descriptions) file path' do
- let(:project) { create(:project) }
- let(:untracked_file) { create_untracked_file("/#{project.full_path}/#{project.uploads.first.path}") }
-
- before do
- UploadService.new(project, uploaded_file, FileUploader).execute
- end
-
- it 'returns the file size' do
- expect(untracked_file.file_size).to eq(35255)
- end
-
- it 'returns the same thing that CarrierWave would return' do
- expect(untracked_file.file_size).to eq(project.uploads.first.size)
- end
- end
- end
-
- def create_untracked_file(path_relative_to_upload_dir)
- described_class.create!(path: "#{Gitlab::BackgroundMigration::PrepareUntrackedUploads::RELATIVE_UPLOAD_DIR}#{path_relative_to_upload_dir}")
end
end
diff --git a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
index 370c2490b97..35750d89c35 100644
--- a/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
+++ b/spec/lib/gitlab/background_migration/prepare_untracked_uploads_spec.rb
@@ -1,20 +1,16 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do
- include TrackUntrackedUploadsHelpers
- include MigrationsHelpers
-
- let!(:untracked_files_for_uploads) { described_class::UntrackedFile }
-
- before do
- DatabaseCleaner.clean
-
- drop_temp_table_if_exists
- end
-
- after do
- drop_temp_table_if_exists
- end
+# Rollback DB to 10.5 (later than this was originally written for) because it still needs to work.
+describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq, :migration, schema: 20180208183958 do
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
+
+ let!(:untracked_files_for_uploads) { table(:untracked_files_for_uploads) }
+ let!(:appearances) { table(:appearances) }
+ let!(:namespaces) { table(:namespaces) }
+ let!(:projects) { table(:projects) }
+ let!(:routes) { table(:routes) }
+ let!(:uploads) { table(:uploads) }
+ let!(:users) { table(:users) }
around do |example|
# Especially important so the follow-up migration does not get run
@@ -23,71 +19,34 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do
end
end
- # E.g. The installation is in use at the time of migration, and someone has
- # just uploaded a file
- shared_examples 'does not add files in /uploads/tmp' do
- let(:tmp_file) { Rails.root.join(described_class::ABSOLUTE_UPLOAD_DIR, 'tmp', 'some_file.jpg') }
-
- before do
- FileUtils.mkdir(File.dirname(tmp_file))
- FileUtils.touch(tmp_file)
- end
-
- after do
- FileUtils.rm(tmp_file)
- end
-
- it 'does not add files from /uploads/tmp' do
- described_class.new.perform
-
- expect(untracked_files_for_uploads.count).to eq(5)
- end
- end
-
- it 'ensures the untracked_files_for_uploads table exists' do
- expect do
- described_class.new.perform
- end.to change { ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads) }.from(false).to(true)
- end
-
- it 'has a path field long enough for really long paths' do
- described_class.new.perform
-
- component = 'a' * 255
-
- long_path = [
- 'uploads',
- component, # project.full_path
- component # filename
- ].flatten.join('/')
+ shared_examples 'prepares the untracked_files_for_uploads table' do
+ context 'when files were uploaded before and after hashed storage was enabled' do
+ let!(:appearance) { create_or_update_appearance(logo: true, header_logo: true) }
+ let!(:user) { create_user(avatar: true) }
+ let!(:project1) { create_project(avatar: true) }
+ let(:project2) { create_project } # instantiate after enabling hashed_storage
- record = untracked_files_for_uploads.create!(path: long_path)
- expect(record.reload.path.size).to eq(519)
- end
+ before do
+ # Markdown upload before enabling hashed_storage
+ add_markdown_attachment(project1)
- context "test bulk insert with ON CONFLICT DO NOTHING or IGNORE" do
- around do |example|
- # If this is CI, we use Postgres 9.2 so this whole context should be
- # skipped since we're unable to use ON CONFLICT DO NOTHING or IGNORE.
- if described_class.new.send(:can_bulk_insert_and_ignore_duplicates?)
- example.run
+ # Markdown upload after enabling hashed_storage
+ add_markdown_attachment(project2, hashed_storage: true)
end
- end
- context 'when files were uploaded before and after hashed storage was enabled' do
- let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) }
- let!(:user) { create(:user, :with_avatar) }
- let!(:project1) { create(:project, :with_avatar) }
- let(:project2) { create(:project) } # instantiate after enabling hashed_storage
+ it 'has a path field long enough for really long paths' do
+ described_class.new.perform
- before do
- # Markdown upload before enabling hashed_storage
- UploadService.new(project1, uploaded_file, FileUploader).execute
+ component = 'a' * 255
- stub_application_setting(hashed_storage_enabled: true)
+ long_path = [
+ 'uploads',
+ component, # project.full_path
+ component # filename
+ ].flatten.join('/')
- # Markdown upload after enabling hashed_storage
- UploadService.new(project2, uploaded_file, FileUploader).execute
+ record = untracked_files_for_uploads.create!(path: long_path)
+ expect(record.reload.path.size).to eq(519)
end
it 'adds unhashed files to the untracked_files_for_uploads table' do
@@ -106,14 +65,15 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do
it 'does not add hashed files to the untracked_files_for_uploads table' do
described_class.new.perform
- hashed_file_path = project2.uploads.where(uploader: 'FileUploader').first.path
+ hashed_file_path = get_uploads(project2, 'Project').where(uploader: 'FileUploader').first.path
expect(untracked_files_for_uploads.where("path like '%#{hashed_file_path}%'").exists?).to be_falsey
end
it 'correctly schedules the follow-up background migration jobs' do
described_class.new.perform
- expect(described_class::FOLLOW_UP_MIGRATION).to be_scheduled_migration(1, 5)
+ ids = described_class::UntrackedFile.all.order(:id).pluck(:id)
+ expect(described_class::FOLLOW_UP_MIGRATION).to be_scheduled_migration(ids.first, ids.last)
expect(BackgroundMigrationWorker.jobs.size).to eq(1)
end
@@ -130,91 +90,68 @@ describe Gitlab::BackgroundMigration::PrepareUntrackedUploads, :sidekiq do
end
end
+ # E.g. The installation is in use at the time of migration, and someone has
+ # just uploaded a file
context 'when there are files in /uploads/tmp' do
- it_behaves_like 'does not add files in /uploads/tmp'
- end
- end
- end
-
- context 'test bulk insert without ON CONFLICT DO NOTHING or IGNORE' do
- before do
- # If this is CI, we use Postgres 9.2 so this stub has no effect.
- #
- # If this is being run on Postgres 9.5+ or MySQL, then this stub allows us
- # to test the bulk insert functionality without ON CONFLICT DO NOTHING or
- # IGNORE.
- allow_any_instance_of(described_class).to receive(:postgresql_pre_9_5?).and_return(true)
- end
-
- context 'when files were uploaded before and after hashed storage was enabled' do
- let!(:appearance) { create_or_update_appearance(logo: uploaded_file, header_logo: uploaded_file) }
- let!(:user) { create(:user, :with_avatar) }
- let!(:project1) { create(:project, :with_avatar) }
- let(:project2) { create(:project) } # instantiate after enabling hashed_storage
-
- before do
- # Markdown upload before enabling hashed_storage
- UploadService.new(project1, uploaded_file, FileUploader).execute
-
- stub_application_setting(hashed_storage_enabled: true)
+ let(:tmp_file) { Rails.root.join(described_class::ABSOLUTE_UPLOAD_DIR, 'tmp', 'some_file.jpg') }
- # Markdown upload after enabling hashed_storage
- UploadService.new(project2, uploaded_file, FileUploader).execute
- end
-
- it 'adds unhashed files to the untracked_files_for_uploads table' do
- described_class.new.perform
-
- expect(untracked_files_for_uploads.count).to eq(5)
- end
-
- it 'adds files with paths relative to CarrierWave.root' do
- described_class.new.perform
- untracked_files_for_uploads.all.each do |file|
- expect(file.path.start_with?('uploads/')).to be_truthy
+ before do
+ FileUtils.mkdir(File.dirname(tmp_file))
+ FileUtils.touch(tmp_file)
end
- end
-
- it 'does not add hashed files to the untracked_files_for_uploads table' do
- described_class.new.perform
- hashed_file_path = project2.uploads.where(uploader: 'FileUploader').first.path
- expect(untracked_files_for_uploads.where("path like '%#{hashed_file_path}%'").exists?).to be_falsey
- end
+ after do
+ FileUtils.rm(tmp_file)
+ end
- it 'correctly schedules the follow-up background migration jobs' do
- described_class.new.perform
+ it 'does not add files from /uploads/tmp' do
+ described_class.new.perform
- expect(described_class::FOLLOW_UP_MIGRATION).to be_scheduled_migration(1, 5)
- expect(BackgroundMigrationWorker.jobs.size).to eq(1)
+ expect(untracked_files_for_uploads.count).to eq(5)
+ end
end
- # E.g. from a previous failed run of this background migration
- context 'when there is existing data in untracked_files_for_uploads' do
- before do
- described_class.new.perform
- end
+ context 'when the last batch size exactly matches the max batch size' do
+ it 'does not raise error' do
+ stub_const("#{described_class}::FIND_BATCH_SIZE", 5)
- it 'does not error or produce duplicates of existing data' do
expect do
described_class.new.perform
- end.not_to change { untracked_files_for_uploads.count }.from(5)
+ end.not_to raise_error
+
+ expect(untracked_files_for_uploads.count).to eq(5)
end
end
+ end
+ end
- context 'when there are files in /uploads/tmp' do
- it_behaves_like 'does not add files in /uploads/tmp'
- end
+ # If running on Postgres 9.2 (like on CI), this whole context is skipped
+ # since we're unable to use ON CONFLICT DO NOTHING or IGNORE.
+ context "test bulk insert with ON CONFLICT DO NOTHING or IGNORE", if: described_class.new.send(:can_bulk_insert_and_ignore_duplicates?) do
+ it_behaves_like 'prepares the untracked_files_for_uploads table'
+ end
+
+ # If running on Postgres 9.2 (like on CI), the stubbed method has no effect.
+ #
+ # If running on Postgres 9.5+ or MySQL, then this context effectively tests
+ # the bulk insert functionality without ON CONFLICT DO NOTHING or IGNORE.
+ context 'test bulk insert without ON CONFLICT DO NOTHING or IGNORE' do
+ before do
+ allow_any_instance_of(described_class).to receive(:postgresql_pre_9_5?).and_return(true)
end
+
+ it_behaves_like 'prepares the untracked_files_for_uploads table'
end
# Very new or lightly-used installations that are running this migration
# may not have an upload directory because they have no uploads.
context 'when no files were ever uploaded' do
- it 'does not add to the untracked_files_for_uploads table (and does not raise error)' do
- described_class.new.perform
+ it 'deletes the `untracked_files_for_uploads` table (and does not raise error)' do
+ background_migration = described_class.new
+
+ expect(background_migration).to receive(:drop_temp_table)
- expect(untracked_files_for_uploads.count).to eq(0)
+ background_migration.perform
end
end
end
diff --git a/spec/lib/gitlab/bare_repository_import/importer_spec.rb b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
index f302e412a6e..eb4b9d8b12f 100644
--- a/spec/lib/gitlab/bare_repository_import/importer_spec.rb
+++ b/spec/lib/gitlab/bare_repository_import/importer_spec.rb
@@ -8,11 +8,15 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
subject(:importer) { described_class.new(admin, bare_repository) }
before do
+ @rainbow = Rainbow.enabled
+ Rainbow.enabled = false
+
allow(described_class).to receive(:log)
end
after do
FileUtils.rm_rf(base_dir)
+ Rainbow.enabled = @rainbow
end
shared_examples 'importing a repository' do
@@ -148,7 +152,7 @@ describe Gitlab::BareRepositoryImport::Importer, repository: true do
# This is a quick way to get a valid repository instead of copying an
# existing one. Since it's not persisted, the importer will try to
# create the project.
- project = build(:project, :repository)
+ project = build(:project, :legacy_storage, :repository)
original_commit_count = project.repository.commit_count
bare_repo = Gitlab::BareRepositoryImport::Repository.new(project.repository_storage_path, project.repository.path)
diff --git a/spec/lib/gitlab/checks/change_access_spec.rb b/spec/lib/gitlab/checks/change_access_spec.rb
index 475b5c5cfb2..b49ddbfc780 100644
--- a/spec/lib/gitlab/checks/change_access_spec.rb
+++ b/spec/lib/gitlab/checks/change_access_spec.rb
@@ -190,7 +190,7 @@ describe Gitlab::Checks::ChangeAccess do
context 'with LFS not enabled' do
it 'skips the validation' do
- expect_any_instance_of(described_class).not_to receive(:lfs_file_locks_validation)
+ expect_any_instance_of(Gitlab::Checks::CommitCheck).not_to receive(:validate)
subject.exec
end
@@ -207,7 +207,7 @@ describe Gitlab::Checks::ChangeAccess do
end
end
- context 'when change is sent by the author od the lock' do
+ context 'when change is sent by the author of the lock' do
let(:user) { owner }
it "doesn't raise any error" do
diff --git a/spec/lib/gitlab/closing_issue_extractor_spec.rb b/spec/lib/gitlab/closing_issue_extractor_spec.rb
index 28c679af12a..8d4862932b2 100644
--- a/spec/lib/gitlab/closing_issue_extractor_spec.rb
+++ b/spec/lib/gitlab/closing_issue_extractor_spec.rb
@@ -365,6 +365,20 @@ describe Gitlab::ClosingIssueExtractor do
.to match_array([issue, other_issue, third_issue])
end
+ it 'allows oxford commas (comma before and) when referencing multiple issues' do
+ message = "Closes #{reference}, #{reference2}, and #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
+ it 'allows spaces before commas when referencing multiple issues' do
+ message = "Closes #{reference} , #{reference2} , and #{reference3}"
+
+ expect(subject.closed_by_message(message))
+ .to match_array([issue, other_issue, third_issue])
+ end
+
it 'fetches issues in multi-line message' do
message = "Awesome commit (closes #{reference})\nAlso fixes #{reference2}"
diff --git a/spec/lib/gitlab/contributions_calendar_spec.rb b/spec/lib/gitlab/contributions_calendar_spec.rb
index f1655854486..49a179ba875 100644
--- a/spec/lib/gitlab/contributions_calendar_spec.rb
+++ b/spec/lib/gitlab/contributions_calendar_spec.rb
@@ -118,6 +118,19 @@ describe Gitlab::ContributionsCalendar do
expect(calendar.events_by_date(today)).to contain_exactly(e1)
expect(calendar(contributor).events_by_date(today)).to contain_exactly(e1, e2, e3)
end
+
+ context 'when the user cannot read read cross project' do
+ before do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ it 'does not return any events' do
+ create_event(public_project, today)
+
+ expect(calendar(user).events_by_date(today)).to be_empty
+ end
+ end
end
describe '#starting_year' do
diff --git a/spec/lib/gitlab/cross_project_access/check_collection_spec.rb b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
new file mode 100644
index 00000000000..a9e7575240e
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access/check_collection_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess::CheckCollection do
+ subject(:collection) { described_class.new }
+
+ describe '#add_collection' do
+ it 'merges the checks of 2 collections' do
+ initial_check = double('check')
+ collection.add_check(initial_check)
+
+ other_collection = described_class.new
+ other_check = double('other_check')
+ other_collection.add_check(other_check)
+
+ shared_check = double('shared check')
+ other_collection.add_check(shared_check)
+ collection.add_check(shared_check)
+
+ collection.add_collection(other_collection)
+
+ expect(collection.checks).to contain_exactly(initial_check, shared_check, other_check)
+ end
+ end
+
+ describe '#should_run?' do
+ def fake_check(run, skip)
+ check = double("Check: run=#{run} - skip={skip}")
+ allow(check).to receive(:should_run?).and_return(run)
+ allow(check).to receive(:should_skip?).and_return(skip)
+ allow(check).to receive(:skip).and_return(skip)
+
+ check
+ end
+
+ it 'returns true if one of the check says it should run' do
+ check = fake_check(true, false)
+ other_check = fake_check(false, false)
+
+ collection.add_check(check)
+ collection.add_check(other_check)
+
+ expect(collection.should_run?(double)).to be_truthy
+ end
+
+ it 'returns false if one of the check says it should be skipped' do
+ check = fake_check(true, false)
+ other_check = fake_check(false, true)
+
+ collection.add_check(check)
+ collection.add_check(other_check)
+
+ expect(collection.should_run?(double)).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cross_project_access/check_info_spec.rb b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
new file mode 100644
index 00000000000..bc9dbf2bece
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access/check_info_spec.rb
@@ -0,0 +1,111 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess::CheckInfo do
+ let(:dummy_controller) { double }
+
+ before do
+ allow(dummy_controller).to receive(:action_name).and_return('index')
+ end
+
+ describe '#should_run?' do
+ it 'runs when an action is defined' do
+ info = described_class.new({ index: true }, nil, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'runs when the action is missing' do
+ info = described_class.new({}, nil, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not run when the action is excluded' do
+ info = described_class.new({ index: false }, nil, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_falsy
+ end
+
+ it 'runs when the `if` conditional is true' do
+ info = described_class.new({}, -> { true }, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not run when the if condition is false' do
+ info = described_class.new({}, -> { false }, nil, false)
+
+ expect(info.should_run?(dummy_controller)).to be_falsy
+ end
+
+ it 'does not run when the `unless` check is true' do
+ info = described_class.new({}, nil, -> { true }, false)
+
+ expect(info.should_run?(dummy_controller)).to be_falsy
+ end
+
+ it 'runs when the `unless` check is false' do
+ info = described_class.new({}, nil, -> { false }, false)
+
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+
+ it 'returns the the oposite of #should_skip? when the check is a skip' do
+ info = described_class.new({}, nil, nil, true)
+
+ expect(info).to receive(:should_skip?).with(dummy_controller).and_return(false)
+ expect(info.should_run?(dummy_controller)).to be_truthy
+ end
+ end
+
+ describe '#should_skip?' do
+ it 'skips when an action is defined' do
+ info = described_class.new({ index: true }, nil, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not skip when the action is not defined' do
+ info = described_class.new({}, nil, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'does not skip when the action is excluded' do
+ info = described_class.new({ index: false }, nil, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'skips when the `if` conditional is true' do
+ info = described_class.new({ index: true }, -> { true }, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+
+ it 'does not skip the `if` conditional is false' do
+ info = described_class.new({ index: true }, -> { false }, nil, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'does not skip when the `unless` check is true' do
+ info = described_class.new({ index: true }, nil, -> { true }, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_falsy
+ end
+
+ it 'skips when `unless` check is false' do
+ info = described_class.new({ index: true }, nil, -> { false }, true)
+
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+
+ it 'returns the the oposite of #should_run? when the check is not a skip' do
+ info = described_class.new({}, nil, nil, false)
+
+ expect(info).to receive(:should_run?).with(dummy_controller).and_return(false)
+ expect(info.should_skip?(dummy_controller)).to be_truthy
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cross_project_access/class_methods_spec.rb b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
new file mode 100644
index 00000000000..5349685e633
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access/class_methods_spec.rb
@@ -0,0 +1,46 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess::ClassMethods do
+ let(:dummy_class) do
+ Class.new do
+ extend Gitlab::CrossProjectAccess::ClassMethods
+ end
+ end
+ let(:dummy_proc) { lambda { false } }
+
+ describe '#requires_cross_project_access' do
+ it 'creates a correct check when a hash is passed' do
+ expect(Gitlab::CrossProjectAccess)
+ .to receive(:add_check).with(dummy_class,
+ actions: { hello: true, world: false },
+ positive_condition: dummy_proc,
+ negative_condition: dummy_proc)
+
+ dummy_class.requires_cross_project_access(
+ hello: true, world: false, if: dummy_proc, unless: dummy_proc
+ )
+ end
+
+ it 'creates a correct check when an array is passed' do
+ expect(Gitlab::CrossProjectAccess)
+ .to receive(:add_check).with(dummy_class,
+ actions: { hello: true, world: true },
+ positive_condition: nil,
+ negative_condition: nil)
+
+ dummy_class.requires_cross_project_access(:hello, :world)
+ end
+
+ it 'creates a correct check when an array and a hash is passed' do
+ expect(Gitlab::CrossProjectAccess)
+ .to receive(:add_check).with(dummy_class,
+ actions: { hello: true, world: true },
+ positive_condition: dummy_proc,
+ negative_condition: dummy_proc)
+
+ dummy_class.requires_cross_project_access(
+ :hello, :world, if: dummy_proc, unless: dummy_proc
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/cross_project_access_spec.rb b/spec/lib/gitlab/cross_project_access_spec.rb
new file mode 100644
index 00000000000..614b0473c7e
--- /dev/null
+++ b/spec/lib/gitlab/cross_project_access_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Gitlab::CrossProjectAccess do
+ let(:super_class) { Class.new }
+ let(:descendant_class) { Class.new(super_class) }
+ let(:current_instance) { described_class.new }
+
+ before do
+ allow(described_class).to receive(:instance).and_return(current_instance)
+ end
+
+ describe '#add_check' do
+ it 'keeps track of the properties to check' do
+ expect do
+ described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { true },
+ negative_condition: -> { false })
+ end.to change { described_class.checks.size }.by(1)
+ end
+
+ it 'builds the check correctly' do
+ check_collection = described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { 'positive' },
+ negative_condition: -> { 'negative' })
+
+ check = check_collection.checks.first
+
+ expect(check.actions).to eq(index: true)
+ expect(check.positive_condition.call).to eq('positive')
+ expect(check.negative_condition.call).to eq('negative')
+ end
+
+ it 'merges the checks of a parent class into existing checks of a subclass' do
+ subclass_collection = described_class.add_check(descendant_class)
+
+ expect(subclass_collection).to receive(:add_collection).and_call_original
+
+ described_class.add_check(super_class)
+ end
+
+ it 'merges the existing checks of a superclass into the checks of a subclass' do
+ super_collection = described_class.add_check(super_class)
+ descendant_collection = described_class.add_check(descendant_class)
+
+ expect(descendant_collection.checks).to include(*super_collection.checks)
+ end
+ end
+
+ describe '#find_check' do
+ it 'returns a check when it was defined for a superclass' do
+ expected_check = described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { 'positive' },
+ negative_condition: -> { 'negative' })
+
+ expect(described_class.find_check(descendant_class.new))
+ .to eq(expected_check)
+ end
+
+ it 'caches the result for a subclass' do
+ described_class.add_check(super_class,
+ actions: { index: true },
+ positive_condition: -> { 'positive' },
+ negative_condition: -> { 'negative' })
+
+ expect(described_class.instance).to receive(:closest_parent).once.and_call_original
+
+ 2.times { described_class.find_check(descendant_class.new) }
+ end
+
+ it 'returns the checks for the closest class if there are more checks available' do
+ described_class.add_check(super_class,
+ actions: { index: true })
+ expected_check = described_class.add_check(descendant_class,
+ actions: { index: true, show: false })
+
+ check = described_class.find_check(descendant_class.new)
+
+ expect(check).to eq(expected_check)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index f31475dbd71..b411aaa19da 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -94,7 +94,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
describe '#move_repositories' do
let(:namespace) { create(:group, name: 'hello-group') }
it 'moves a project for a namespace' do
- create(:project, :repository, namespace: namespace, path: 'hello-project')
+ create(:project, :repository, :legacy_storage, namespace: namespace, path: 'hello-project')
expected_path = File.join(TestEnv.repos_path, 'bye-group', 'hello-project.git')
subject.move_repositories(namespace, 'hello-group', 'bye-group')
@@ -104,7 +104,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
it 'moves a namespace in a subdirectory correctly' do
child_namespace = create(:group, name: 'sub-group', parent: namespace)
- create(:project, :repository, namespace: child_namespace, path: 'hello-project')
+ create(:project, :repository, :legacy_storage, namespace: child_namespace, path: 'hello-project')
expected_path = File.join(TestEnv.repos_path, 'hello-group', 'renamed-sub-group', 'hello-project.git')
@@ -115,7 +115,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
it 'moves a parent namespace with subdirectories' do
child_namespace = create(:group, name: 'sub-group', parent: namespace)
- create(:project, :repository, namespace: child_namespace, path: 'hello-project')
+ create(:project, :repository, :legacy_storage, namespace: child_namespace, path: 'hello-project')
expected_path = File.join(TestEnv.repos_path, 'renamed-group', 'sub-group', 'hello-project.git')
subject.move_repositories(child_namespace, 'hello-group', 'renamed-group')
@@ -166,7 +166,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
describe '#rename_namespace_dependencies' do
it "moves the the repository for a project in the namespace" do
- create(:project, :repository, namespace: namespace, path: "the-path-project")
+ create(:project, :repository, :legacy_storage, namespace: namespace, path: "the-path-project")
expected_repo = File.join(TestEnv.repos_path, "the-path0", "the-path-project.git")
subject.rename_namespace_dependencies(namespace, 'the-path', 'the-path0')
@@ -187,7 +187,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
end
it 'invalidates the markdown cache of related projects' do
- project = create(:project, namespace: namespace, path: "the-path-project")
+ project = create(:project, :legacy_storage, namespace: namespace, path: "the-path-project")
expect(subject).to receive(:remove_cached_html_for_projects).with([project.id])
@@ -243,7 +243,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
describe '#revert_renames', :redis do
it 'renames the routes back to the previous values' do
- project = create(:project, :repository, path: 'a-project', namespace: namespace)
+ project = create(:project, :legacy_storage, :repository, path: 'a-project', namespace: namespace)
subject.rename_namespace(namespace)
expect(subject).to receive(:perform_rename)
@@ -261,7 +261,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :
end
it 'moves the repositories back to their original place' do
- project = create(:project, :repository, path: 'a-project', namespace: namespace)
+ project = create(:project, :repository, :legacy_storage, path: 'a-project', namespace: namespace)
project.create_repository
subject.rename_namespace(namespace)
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index 0958144643b..b4896d69077 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -5,6 +5,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
let(:subject) { described_class.new(['the-path'], migration) }
let(:project) do
create(:project,
+ :legacy_storage,
path: 'the-path',
namespace: create(:namespace, path: 'known-parent' ))
end
@@ -17,7 +18,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
describe '#projects_for_paths' do
it 'searches using nested paths' do
namespace = create(:namespace, path: 'hello')
- project = create(:project, path: 'THE-path', namespace: namespace)
+ project = create(:project, :legacy_storage, path: 'THE-path', namespace: namespace)
result_ids = described_class.new(['Hello/the-path'], migration)
.projects_for_paths.map(&:id)
@@ -26,8 +27,8 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
end
it 'includes the correct projects' do
- project = create(:project, path: 'THE-path')
- _other_project = create(:project)
+ project = create(:project, :legacy_storage, path: 'THE-path')
+ _other_project = create(:project, :legacy_storage)
result_ids = subject.projects_for_paths.map(&:id)
@@ -36,7 +37,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
end
describe '#rename_projects' do
- let!(:projects) { create_list(:project, 2, path: 'the-path') }
+ let!(:projects) { create_list(:project, 2, :legacy_storage, path: 'the-path') }
it 'renames each project' do
expect(subject).to receive(:rename_project).twice
@@ -120,7 +121,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :de
describe '#move_repository' do
let(:known_parent) { create(:namespace, path: 'known-parent') }
- let(:project) { create(:project, :repository, path: 'the-path', namespace: known_parent) }
+ let(:project) { create(:project, :repository, :legacy_storage, path: 'the-path', namespace: known_parent) }
it 'moves the repository for a project' do
expected_path = File.join(TestEnv.repos_path, 'known-parent', 'new-repo.git')
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
index cd602ccab8e..73d60c021c8 100644
--- a/spec/lib/gitlab/diff/highlight_spec.rb
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -72,6 +72,28 @@ describe Gitlab::Diff::Highlight do
expect(subject[5].text).to eq(code)
expect(subject[5].text).to be_html_safe
end
+
+ context 'when the inline diff marker has an invalid range' do
+ before do
+ allow_any_instance_of(Gitlab::Diff::InlineDiffMarker).to receive(:mark).and_raise(RangeError)
+ end
+
+ it 'keeps the original rich line' do
+ code = %q{+ raise RuntimeError, "System commands must be given as an array of strings"}
+
+ expect(subject[5].text).to eq(code)
+ expect(subject[5].text).not_to be_html_safe
+ end
+
+ it 'reports to Sentry if configured' do
+ allow(Gitlab::Sentry).to receive(:enabled?).and_return(true)
+
+ expect(Gitlab::Sentry).to receive(:context)
+ expect(Raven).to receive(:capture_exception)
+
+ subject
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/email/attachment_uploader_spec.rb b/spec/lib/gitlab/email/attachment_uploader_spec.rb
index f61dbc67ad1..45c690842bc 100644
--- a/spec/lib/gitlab/email/attachment_uploader_spec.rb
+++ b/spec/lib/gitlab/email/attachment_uploader_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
describe Gitlab::Email::AttachmentUploader do
describe "#execute" do
- let(:project) { build(:project) }
+ let(:project) { create(:project) }
let(:message_raw) { fixture_file("emails/attachment.eml") }
let(:message) { Mail::Message.new(message_raw) }
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index 4e9367323cb..83d431a7458 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -24,6 +24,11 @@ describe Gitlab::EncodingHelper do
'removes invalid bytes from ASCII-8bit encoded multibyte string. This can occur when a git diff match line truncates in the middle of a multibyte character. This occurs after the second word in this example. The test string is as short as we can get while still triggering the error condition when not looking at `detect[:confidence]`.',
"mu ns\xC3\n Lorem ipsum dolor sit amet, consectetur adipisicing ut\xC3\xA0y\xC3\xB9abcd\xC3\xB9efg kia elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non p\n {: .normal_pn}\n \n-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in\n# *Lorem ipsum\xC3\xB9l\xC3\xB9l\xC3\xA0 dolor\xC3\xB9k\xC3\xB9 sit\xC3\xA8b\xC3\xA8 N\xC3\xA8 amet b\xC3\xA0d\xC3\xAC*\n+# *consectetur\xC3\xB9l\xC3\xB9l\xC3\xA0 adipisicing\xC3\xB9k\xC3\xB9 elit\xC3\xA8b\xC3\xA8 N\xC3\xA8 sed do\xC3\xA0d\xC3\xAC*{: .italic .smcaps}\n \n \xEF\x9B\xA1 eiusmod tempor incididunt, ut\xC3\xAAn\xC3\xB9 labore et dolore. Tw\xC4\x83nj\xC3\xAC magna aliqua. Ut enim ad minim veniam\n {: .normal}\n@@ -9,5 +9,5 @@ quis nostrud\xC3\xAAt\xC3\xB9 exercitiation ullamco laboris m\xC3\xB9s\xC3\xB9k\xC3\xB9abc\xC3\xB9 nisi ".force_encoding('ASCII-8BIT'),
"mu ns\n Lorem ipsum dolor sit amet, consectetur adipisicing ut\xC3\xA0y\xC3\xB9abcd\xC3\xB9efg kia elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non p\n {: .normal_pn}\n \n-Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in\n# *Lorem ipsum\xC3\xB9l\xC3\xB9l\xC3\xA0 dolor\xC3\xB9k\xC3\xB9 sit\xC3\xA8b\xC3\xA8 N\xC3\xA8 amet b\xC3\xA0d\xC3\xAC*\n+# *consectetur\xC3\xB9l\xC3\xB9l\xC3\xA0 adipisicing\xC3\xB9k\xC3\xB9 elit\xC3\xA8b\xC3\xA8 N\xC3\xA8 sed do\xC3\xA0d\xC3\xAC*{: .italic .smcaps}\n \n \xEF\x9B\xA1 eiusmod tempor incididunt, ut\xC3\xAAn\xC3\xB9 labore et dolore. Tw\xC4\x83nj\xC3\xAC magna aliqua. Ut enim ad minim veniam\n {: .normal}\n@@ -9,5 +9,5 @@ quis nostrud\xC3\xAAt\xC3\xB9 exercitiation ullamco laboris m\xC3\xB9s\xC3\xB9k\xC3\xB9abc\xC3\xB9 nisi "
+ ],
+ [
+ 'string with detected encoding that is not supported in Ruby',
+ "\xFFe,i\xFF,\xB8oi,'\xB8,\xFF,-",
+ "--broken encoding: IBM420_ltr"
]
].each do |description, test_string, xpect|
it description do
diff --git a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
index 326ed2f2ecf..13df8531b63 100644
--- a/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
+++ b/spec/lib/gitlab/gfm/uploads_rewriter_spec.rb
@@ -39,8 +39,8 @@ describe Gitlab::Gfm::UploadsRewriter do
it 'copies files' do
expect(new_files).to all(exist)
expect(old_paths).not_to match_array new_paths
- expect(old_paths).to all(include(old_project.full_path))
- expect(new_paths).to all(include(new_project.full_path))
+ expect(old_paths).to all(include(old_project.disk_path))
+ expect(new_paths).to all(include(new_project.disk_path))
end
it 'does not remove old files' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 59e9e1cc94c..a6341cd509b 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -276,6 +276,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
expect(blobs.count).to eq(1)
expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ expect(blobs).to be_an(Array)
end
it 'accepts blob IDs as a lazy enumerator' do
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 85e6efd7ca2..0b20a6349a2 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -102,6 +102,10 @@ describe Gitlab::Git::Commit, seed_helper: true do
expect(described_class.find(repository, SeedRepo::Commit::ID)).to be_valid_commit
end
+ it "returns an array of parent ids" do
+ expect(described_class.find(repository, SeedRepo::Commit::ID).parent_ids).to be_an(Array)
+ end
+
it "should return valid commit for tag" do
expect(described_class.find(repository, 'v1.0.0').id).to eq('6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9')
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index edcf8889c27..13358995383 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require "spec_helper"
describe Gitlab::Git::Repository, seed_helper: true do
@@ -599,6 +600,33 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#branch_names_contains_sha' do
+ shared_examples 'returning the right branches' do
+ let(:head_id) { repository.rugged.head.target.oid }
+ let(:new_branch) { head_id }
+
+ before do
+ repository.create_branch(new_branch, 'master')
+ end
+
+ after do
+ repository.delete_branch(new_branch)
+ end
+
+ it 'displays that branch' do
+ expect(repository.branch_names_contains_sha(head_id)).to include('master', new_branch)
+ end
+ end
+
+ context 'when Gitaly is enabled' do
+ it_behaves_like 'returning the right branches'
+ end
+
+ context 'when Gitaly is disabled', :disable_gitaly do
+ it_behaves_like 'returning the right branches'
+ end
+ end
+
describe "#refs_hash" do
subject { repository.refs_hash }
@@ -1589,7 +1617,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
expected_languages = [
{ value: 66.63, label: "Ruby", color: "#701516", highlight: "#701516" },
{ value: 22.96, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
- { value: 7.9, label: "HTML", color: "#e44b23", highlight: "#e44b23" },
+ { value: 7.9, label: "HTML", color: "#e34c26", highlight: "#e34c26" },
{ value: 2.51, label: "CoffeeScript", color: "#244776", highlight: "#244776" }
]
@@ -2203,7 +2231,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
context 'sparse checkout', :skip_gitaly_mock do
let(:expected_files) { %w(files files/js files/js/application.js) }
- before do
+ it 'checks out only the files in the diff' do
allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
m.call(*args) do
worktree_path = args[0]
@@ -2215,11 +2243,59 @@ describe Gitlab::Git::Repository, seed_helper: true do
expect(Dir[files_pattern]).to eq(expected)
end
end
- end
- it 'checkouts only the files in the diff' do
subject
end
+
+ context 'when the diff contains a rename' do
+ let(:repo) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged }
+ let(:end_sha) { new_commit_move_file(repo).oid }
+
+ after do
+ # Erase our commits so other tests get the original repo
+ repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ repo.references.update('refs/heads/master', SeedRepo::LastCommit::ID)
+ end
+
+ it 'does not include the renamed file in the sparse checkout' do
+ allow(repository).to receive(:with_worktree).and_wrap_original do |m, *args|
+ m.call(*args) do
+ worktree_path = args[0]
+ files_pattern = File.join(worktree_path, '**', '*')
+
+ expect(Dir[files_pattern]).not_to include('CHANGELOG')
+ expect(Dir[files_pattern]).not_to include('encoding/CHANGELOG')
+ end
+ end
+
+ subject
+ end
+ end
+ end
+
+ context 'with an ASCII-8BIT diff', :skip_gitaly_mock do
+ let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+✓ testme\n ======\n \n Sample repo for testing gitlab features\n" }
+
+ it 'applies a ASCII-8BIT diff' do
+ allow(repository).to receive(:run_git!).and_call_original
+ allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
+
+ expect(subject).to match(/\h{40}/)
+ end
+ end
+
+ context 'with trailing whitespace in an invalid patch', :skip_gitaly_mock do
+ let(:diff) { "diff --git a/README.md b/README.md\nindex faaf198..43c5edf 100644\n--- a/README.md\n+++ b/README.md\n@@ -1,4 +1,4 @@\n-testme\n+ \n ====== \n \n Sample repo for testing gitlab features\n" }
+
+ it 'does not include whitespace warnings in the error' do
+ allow(repository).to receive(:run_git!).and_call_original
+ allow(repository).to receive(:run_git!).with(%W(diff --binary #{start_sha}...#{end_sha})).and_return(diff.force_encoding('ASCII-8BIT'))
+
+ expect { subject }.to raise_error do |error|
+ expect(error).to be_a(described_class::GitError)
+ expect(error.message).not_to include('trailing whitespace')
+ end
+ end
end
end
end
diff --git a/spec/lib/gitlab/git_access_spec.rb b/spec/lib/gitlab/git_access_spec.rb
index 3c3697e7aa9..6f07e423c1b 100644
--- a/spec/lib/gitlab/git_access_spec.rb
+++ b/spec/lib/gitlab/git_access_spec.rb
@@ -18,8 +18,9 @@ describe Gitlab::GitAccess do
redirected_path: redirected_path)
end
- let(:push_access_check) { access.check('git-receive-pack', '_any') }
- let(:pull_access_check) { access.check('git-upload-pack', '_any') }
+ let(:changes) { '_any' }
+ let(:push_access_check) { access.check('git-receive-pack', changes) }
+ let(:pull_access_check) { access.check('git-upload-pack', changes) }
describe '#check with single protocols allowed' do
def disable_protocol(protocol)
@@ -533,6 +534,19 @@ describe Gitlab::GitAccess do
expect { pull_access_check }.to raise_unauthorized('Your account has been blocked.')
end
+ context 'when the project repository does not exist' do
+ it 'returns not found' do
+ project.add_guest(user)
+ repo = project.repository
+ FileUtils.rm_rf(repo.path)
+
+ # Sanity check for rm_rf
+ expect(repo.exists?).to eq(false)
+
+ expect { pull_access_check }.to raise_error(Gitlab::GitAccess::NotFoundError, 'A repository for this project does not exist yet.')
+ end
+ end
+
describe 'without access to project' do
context 'pull code' do
it { expect { pull_access_check }.to raise_not_found }
@@ -646,6 +660,20 @@ describe Gitlab::GitAccess do
end
end
+ describe 'check LFS integrity' do
+ let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master', '6f6d7e7ed 570e7b2ab refs/heads/feature'] }
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'checks LFS integrity only for first change' do
+ expect_any_instance_of(Gitlab::Checks::LfsIntegrity).to receive(:objects_missing?).exactly(1).times
+
+ push_access_check
+ end
+ end
+
describe '#check_push_access!' do
before do
merge_into_protected_branch
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 186b2d9279d..730ede99fc9 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Gitlab::GitAccessWiki do
let(:access) { described_class.new(user, project, 'web', authentication_abilities: authentication_abilities, redirected_path: redirected_path) }
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :wiki_repo) }
let(:user) { create(:user) }
let(:changes) { ['6f6d7e7ed 570e7b2ab refs/heads/master'] }
let(:redirected_path) { nil }
@@ -48,6 +48,18 @@ describe Gitlab::GitAccessWiki do
it 'give access to download wiki code' do
expect { subject }.not_to raise_error
end
+
+ context 'when the wiki repository does not exist' do
+ it 'returns not found' do
+ wiki_repo = project.wiki.repository
+ FileUtils.rm_rf(wiki_repo.path)
+
+ # Sanity check for rm_rf
+ expect(wiki_repo.exists?).to eq(false)
+
+ expect { subject }.to raise_error(Gitlab::GitAccess::NotFoundError, 'A repository for this project does not exist yet.')
+ end
+ end
end
context 'when wiki feature is disabled' do
diff --git a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
index 001c4d3e10a..9be3fa633a7 100644
--- a/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/commit_service_spec.rb
@@ -113,7 +113,7 @@ describe Gitlab::GitalyClient::CommitService do
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([])
- client.tree_entries(repository, revision, path)
+ client.tree_entries(repository, revision, path, false)
end
context 'with UTF-8 params strings' do
@@ -126,7 +126,7 @@ describe Gitlab::GitalyClient::CommitService do
.with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
.and_return([])
- client.tree_entries(repository, revision, path)
+ client.tree_entries(repository, revision, path, false)
end
end
end
diff --git a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
index cbc7ce1c1b0..c50e73cecfc 100644
--- a/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/repository_service_spec.rb
@@ -84,4 +84,30 @@ describe Gitlab::GitalyClient::RepositoryService do
expect(client.has_local_branches?).to be(true)
end
end
+
+ describe '#rebase_in_progress?' do
+ let(:rebase_id) { 1 }
+
+ it 'sends a repository_rebase_in_progress message' do
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:is_rebase_in_progress)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(in_progress: true))
+
+ client.rebase_in_progress?(rebase_id)
+ end
+ end
+
+ describe '#squash_in_progress?' do
+ let(:squash_id) { 1 }
+
+ it 'sends a repository_squash_in_progress message' do
+ expect_any_instance_of(Gitaly::RepositoryService::Stub)
+ .to receive(:is_squash_in_progress)
+ .with(gitaly_request_with_path(storage_name, relative_path), kind_of(Hash))
+ .and_return(double(in_progress: true))
+
+ client.squash_in_progress?(squash_id)
+ 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 46a57e08963..5bedfc79dd3 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -11,7 +11,8 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
import_source: 'foo/bar',
repository_storage_path: 'foo',
disk_path: 'foo',
- repository: repository
+ repository: repository,
+ create_wiki: true
)
end
@@ -192,7 +193,7 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
expect(importer.import_wiki_repository).to eq(true)
end
- it 'marks the import as failed if an error was raised' do
+ it 'marks the import as failed and creates an empty repo if an error was raised' do
expect(importer.gitlab_shell)
.to receive(:import_repository)
.and_raise(Gitlab::Shell::Error)
@@ -201,6 +202,9 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
.to receive(:fail_import)
.and_return(false)
+ expect(project)
+ .to receive(:create_wiki)
+
expect(importer.import_wiki_repository).to eq(false)
end
end
diff --git a/spec/lib/gitlab/gpg/commit_spec.rb b/spec/lib/gitlab/gpg/commit_spec.rb
index e3bf2801406..67c62458f0f 100644
--- a/spec/lib/gitlab/gpg/commit_spec.rb
+++ b/spec/lib/gitlab/gpg/commit_spec.rb
@@ -49,7 +49,9 @@ describe Gitlab::Gpg::Commit do
end
it 'returns a valid signature' do
- expect(described_class.new(commit).signature).to have_attributes(
+ signature = described_class.new(commit).signature
+
+ expect(signature).to have_attributes(
commit_sha: commit_sha,
project: project,
gpg_key: gpg_key,
@@ -58,9 +60,31 @@ describe Gitlab::Gpg::Commit do
gpg_key_user_email: GpgHelpers::User1.emails.first,
verification_status: 'verified'
)
+ expect(signature.persisted?).to be_truthy
end
it_behaves_like 'returns the cached signature on second call'
+
+ context 'read-only mode' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ end
+
+ it 'does not create a cached signature' do
+ signature = described_class.new(commit).signature
+
+ expect(signature).to have_attributes(
+ commit_sha: commit_sha,
+ project: project,
+ gpg_key: gpg_key,
+ gpg_key_primary_keyid: GpgHelpers::User1.primary_keyid,
+ gpg_key_user_name: GpgHelpers::User1.names.first,
+ gpg_key_user_email: GpgHelpers::User1.emails.first,
+ verification_status: 'verified'
+ )
+ expect(signature.persisted?).to be_falsey
+ end
+ end
end
context 'commit signed with a subkey' do
diff --git a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
index a685521cbf0..8a3a244be21 100644
--- a/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_restorer_spec.rb
@@ -16,7 +16,7 @@ describe Gitlab::ImportExport::UploadsRestorer do
end
describe 'legacy storage' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :legacy_storage) }
subject(:restorer) { described_class.new(project: project, shared: shared) }
@@ -34,7 +34,7 @@ describe Gitlab::ImportExport::UploadsRestorer do
end
describe 'hashed storage' do
- let(:project) { create(:project, :hashed) }
+ let(:project) { create(:project) }
subject(:restorer) { described_class.new(project: project, shared: shared) }
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 959779523f4..177036c109b 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::ImportExport::UploadsSaver do
end
describe 'legacy storage' do
- let(:project) { create(:project) }
+ let(:project) { create(:project, :legacy_storage) }
subject(:saver) { described_class.new(shared: shared, project: project) }
@@ -37,7 +37,7 @@ describe Gitlab::ImportExport::UploadsSaver do
end
describe 'hashed storage' do
- let(:project) { create(:project, :hashed) }
+ let(:project) { create(:project) }
subject(:saver) { described_class.new(shared: shared, project: project) }
diff --git a/spec/lib/gitlab/import_export/wiki_restorer_spec.rb b/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
new file mode 100644
index 00000000000..81b654e9c5f
--- /dev/null
+++ b/spec/lib/gitlab/import_export/wiki_restorer_spec.rb
@@ -0,0 +1,45 @@
+require 'spec_helper'
+
+describe Gitlab::ImportExport::WikiRestorer do
+ describe 'restore a wiki Git repo' do
+ let!(:project_with_wiki) { create(:project, :wiki_repo) }
+ let!(:project_without_wiki) { create(:project) }
+ let!(:project) { create(:project) }
+ let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
+ let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
+ let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_with_wiki, shared: shared) }
+ let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) }
+ let(:restorer) do
+ described_class.new(path_to_bundle: bundle_path,
+ shared: shared,
+ project: project.wiki,
+ wiki_enabled: true)
+ end
+
+ before do
+ allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ bundler.save
+ end
+
+ after do
+ FileUtils.rm_rf(export_path)
+ Gitlab::Shell.new.remove_repository(project_with_wiki.wiki.repository_storage_path, project_with_wiki.wiki.disk_path)
+ Gitlab::Shell.new.remove_repository(project.wiki.repository_storage_path, project.wiki.disk_path)
+ end
+
+ it 'restores the wiki repo successfully' do
+ expect(restorer.restore).to be true
+ end
+
+ describe "no wiki in the bundle" do
+ let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_without_wiki, shared: shared) }
+
+ it 'creates an empty wiki' do
+ expect(restorer.restore).to be true
+
+ expect(project.wiki_repository_exists?).to be true
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ldap/config_spec.rb b/spec/lib/gitlab/ldap/config_spec.rb
index ca2213cd112..e10837578a8 100644
--- a/spec/lib/gitlab/ldap/config_spec.rb
+++ b/spec/lib/gitlab/ldap/config_spec.rb
@@ -5,6 +5,14 @@ describe Gitlab::LDAP::Config do
let(:config) { described_class.new('ldapmain') }
+ describe '.servers' do
+ it 'returns empty array if no server information is available' do
+ allow(Gitlab.config).to receive(:ldap).and_return('enabled' => false)
+
+ expect(described_class.servers).to eq []
+ end
+ end
+
describe '#initialize' do
it 'requires a provider' do
expect { described_class.new }.to raise_error ArgumentError
diff --git a/spec/lib/gitlab/ldap/person_spec.rb b/spec/lib/gitlab/ldap/person_spec.rb
index b54d4000b53..05e1e394bb1 100644
--- a/spec/lib/gitlab/ldap/person_spec.rb
+++ b/spec/lib/gitlab/ldap/person_spec.rb
@@ -66,15 +66,6 @@ describe Gitlab::LDAP::Person do
end
end
- describe '.validate_entry' do
- it 'raises InvalidEntryError' do
- entry['foo'] = 'bar'
-
- expect { described_class.new(entry, 'ldapmain') }
- .to raise_error(Gitlab::LDAP::Person::InvalidEntryError)
- end
- end
-
describe '#name' do
it 'uses the configured name attribute and handles values as an array' do
name = 'John Doe'
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index 60a134be939..b24c9882c0c 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -3,19 +3,30 @@ require 'spec_helper'
describe Gitlab::Middleware::Go do
let(:app) { double(:app) }
let(:middleware) { described_class.new(app) }
+ let(:env) do
+ {
+ 'rack.input' => '',
+ 'REQUEST_METHOD' => 'GET'
+ }
+ end
describe '#call' do
describe 'when go-get=0' do
+ before do
+ env['QUERY_STRING'] = 'go-get=0'
+ end
+
it 'skips go-import generation' do
- env = { 'rack.input' => '',
- 'QUERY_STRING' => 'go-get=0' }
expect(app).to receive(:call).with(env).and_return('no-go')
middleware.call(env)
end
end
describe 'when go-get=1' do
- let(:current_user) { nil }
+ before do
+ env['QUERY_STRING'] = 'go-get=1'
+ env['PATH_INFO'] = "/#{path}"
+ end
shared_examples 'go-get=1' do |enabled_protocol:|
context 'with simple 2-segment project path' do
@@ -54,21 +65,75 @@ describe Gitlab::Middleware::Go do
project.update_attribute(:visibility_level, Project::PRIVATE)
end
- context 'with access to the project' do
+ shared_examples 'unauthorized' do
+ it 'returns the 2-segment group path' do
+ expect_response_with_path(go, enabled_protocol, group.full_path)
+ end
+ end
+
+ context 'when not authenticated' do
+ it_behaves_like 'unauthorized'
+ end
+
+ context 'when authenticated' do
let(:current_user) { project.creator }
before do
project.team.add_master(current_user)
end
- it 'returns the full project path' do
- expect_response_with_path(go, enabled_protocol, project.full_path)
+ shared_examples 'authenticated' do
+ context 'with access to the project' do
+ it 'returns the full project path' do
+ expect_response_with_path(go, enabled_protocol, project.full_path)
+ end
+ end
+
+ context 'without access to the project' do
+ before do
+ project.team.find_member(current_user).destroy
+ end
+
+ it_behaves_like 'unauthorized'
+ end
end
- end
- context 'without access to the project' do
- it 'returns the 2-segment group path' do
- expect_response_with_path(go, enabled_protocol, group.full_path)
+ context 'using warden' do
+ before do
+ env['warden'] = double(authenticate: current_user)
+ end
+
+ context 'when active' do
+ it_behaves_like 'authenticated'
+ end
+
+ context 'when blocked' do
+ before do
+ current_user.block!
+ end
+
+ it_behaves_like 'unauthorized'
+ end
+ end
+
+ context 'using a personal access token' do
+ let(:personal_access_token) { create(:personal_access_token, user: current_user) }
+
+ before do
+ env['HTTP_PRIVATE_TOKEN'] = personal_access_token.token
+ end
+
+ context 'with api scope' do
+ it_behaves_like 'authenticated'
+ end
+
+ context 'with read_user scope' do
+ before do
+ personal_access_token.update_attribute(:scopes, [:read_user])
+ end
+
+ it_behaves_like 'unauthorized'
+ end
end
end
end
@@ -138,12 +203,6 @@ describe Gitlab::Middleware::Go do
end
def go
- env = {
- 'rack.input' => '',
- 'QUERY_STRING' => 'go-get=1',
- 'PATH_INFO' => "/#{path}",
- 'warden' => double(authenticate: current_user)
- }
middleware.call(env)
end
diff --git a/spec/lib/gitlab/middleware/multipart_spec.rb b/spec/lib/gitlab/middleware/multipart_spec.rb
index 8d925460f01..a2ba91dae80 100644
--- a/spec/lib/gitlab/middleware/multipart_spec.rb
+++ b/spec/lib/gitlab/middleware/multipart_spec.rb
@@ -5,15 +5,17 @@ require 'tempfile'
describe Gitlab::Middleware::Multipart do
let(:app) { double(:app) }
let(:middleware) { described_class.new(app) }
+ let(:original_filename) { 'filename' }
it 'opens top-level files' do
Tempfile.open('top-level') do |tempfile|
- env = post_env({ 'file' => tempfile.path }, { 'file.name' => 'filename' }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
+ env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
expect(app).to receive(:call) do |env|
file = Rack::Request.new(env).params['file']
expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path)
+ expect(file.original_filename).to eq(original_filename)
end
middleware.call(env)
@@ -34,13 +36,14 @@ describe Gitlab::Middleware::Multipart do
it 'opens files one level deep' do
Tempfile.open('one-level') do |tempfile|
- in_params = { 'user' => { 'avatar' => { '.name' => 'filename' } } }
+ in_params = { 'user' => { 'avatar' => { '.name' => original_filename } } }
env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
expect(app).to receive(:call) do |env|
file = Rack::Request.new(env).params['user']['avatar']
expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path)
+ expect(file.original_filename).to eq(original_filename)
end
middleware.call(env)
@@ -49,13 +52,14 @@ describe Gitlab::Middleware::Multipart do
it 'opens files two levels deep' do
Tempfile.open('two-levels') do |tempfile|
- in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => 'filename' } } } }
+ in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename } } } }
env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
expect(app).to receive(:call) do |env|
file = Rack::Request.new(env).params['project']['milestone']['themesong']
expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path)
+ expect(file.original_filename).to eq(original_filename)
end
middleware.call(env)
diff --git a/spec/lib/gitlab/o_auth/user_spec.rb b/spec/lib/gitlab/o_auth/user_spec.rb
index 03e0a9e2a03..b8455403bdb 100644
--- a/spec/lib/gitlab/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/o_auth/user_spec.rb
@@ -724,6 +724,10 @@ describe Gitlab::OAuth::User do
it "does not update the user location" do
expect(gl_user.location).not_to eq(info_hash[:address][:country])
end
+
+ it 'does not create associated user synced attributes metadata' do
+ expect(gl_user.user_synced_attributes_metadata).to be_nil
+ end
end
end
diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb
index 4a43dbb2371..f02b1cf55fb 100644
--- a/spec/lib/gitlab/profiler_spec.rb
+++ b/spec/lib/gitlab/profiler_spec.rb
@@ -53,6 +53,15 @@ describe Gitlab::Profiler do
described_class.profile('/', user: user)
end
+ context 'when providing a user without a personal access token' do
+ it 'raises an error' do
+ user = double(:user)
+ allow(user).to receive_message_chain(:personal_access_tokens, :active, :pluck).and_return([])
+
+ expect { described_class.profile('/', user: user) }.to raise_error('Your user must have a personal_access_token')
+ end
+ end
+
it 'uses the private_token for auth if both it and user are set' do
user = double(:user)
user_token = 'user'
diff --git a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
index 2b488101496..c95719eff1d 100644
--- a/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/matched_metrics_query_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do
context 'with one group where two metrics is found' do
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
allow(client).to receive(:label_values).and_return(metric_names)
end
@@ -70,7 +70,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do
context 'with one group where only one metric is found' do
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
allow(client).to receive(:label_values).and_return('metric_a')
end
@@ -99,7 +99,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricsQuery do
let(:second_metric_group) { simple_metric_group(name: 'nameb', metrics: simple_metrics(added_metric_name: 'metric_c')) }
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group, second_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group, second_metric_group])
allow(client).to receive(:label_values).and_return('metric_c')
end
diff --git a/spec/lib/gitlab/prometheus_client_spec.rb b/spec/lib/gitlab/prometheus_client_spec.rb
index 5d86007f71f..4c3b8deefb9 100644
--- a/spec/lib/gitlab/prometheus_client_spec.rb
+++ b/spec/lib/gitlab/prometheus_client_spec.rb
@@ -19,41 +19,41 @@ describe Gitlab::PrometheusClient do
# - execute_query: A query call
shared_examples 'failure response' do
context 'when request returns 400 with an error message' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 400, body: { error: 'bar!' })
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, 'bar!')
+ .to raise_error(Gitlab::PrometheusClient::Error, 'bar!')
expect(req_stub).to have_been_requested
end
end
context 'when request returns 400 without an error message' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 400)
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, 'Bad data received')
+ .to raise_error(Gitlab::PrometheusClient::Error, 'Bad data received')
expect(req_stub).to have_been_requested
end
end
context 'when request returns 500' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 500, body: { message: 'FAIL!' })
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, '500 - {"message":"FAIL!"}')
+ .to raise_error(Gitlab::PrometheusClient::Error, '500 - {"message":"FAIL!"}')
expect(req_stub).to have_been_requested
end
end
context 'when request returns non json data' do
- it 'raises a Gitlab::PrometheusError error' do
+ it 'raises a Gitlab::PrometheusClient::Error error' do
req_stub = stub_prometheus_request(query_url, status: 200, body: 'not json')
expect { execute_query }
- .to raise_error(Gitlab::PrometheusError, 'Parsing response failed')
+ .to raise_error(Gitlab::PrometheusClient::Error, 'Parsing response failed')
expect(req_stub).to have_been_requested
end
end
@@ -65,27 +65,27 @@ describe Gitlab::PrometheusClient do
subject { described_class.new(RestClient::Resource.new(prometheus_url)) }
context 'exceptions are raised' do
- it 'raises a Gitlab::PrometheusError error when a SocketError is rescued' do
+ it 'raises a Gitlab::PrometheusClient::Error error when a SocketError is rescued' do
req_stub = stub_prometheus_request_with_exception(prometheus_url, SocketError)
expect { subject.send(:get, '/', {}) }
- .to raise_error(Gitlab::PrometheusError, "Can't connect to #{prometheus_url}")
+ .to raise_error(Gitlab::PrometheusClient::Error, "Can't connect to #{prometheus_url}")
expect(req_stub).to have_been_requested
end
- it 'raises a Gitlab::PrometheusError error when a SSLError is rescued' do
+ it 'raises a Gitlab::PrometheusClient::Error error when a SSLError is rescued' do
req_stub = stub_prometheus_request_with_exception(prometheus_url, OpenSSL::SSL::SSLError)
expect { subject.send(:get, '/', {}) }
- .to raise_error(Gitlab::PrometheusError, "#{prometheus_url} contains invalid SSL data")
+ .to raise_error(Gitlab::PrometheusClient::Error, "#{prometheus_url} contains invalid SSL data")
expect(req_stub).to have_been_requested
end
- it 'raises a Gitlab::PrometheusError error when a RestClient::Exception is rescued' do
+ it 'raises a Gitlab::PrometheusClient::Error error when a RestClient::Exception is rescued' do
req_stub = stub_prometheus_request_with_exception(prometheus_url, RestClient::Exception)
expect { subject.send(:get, '/', {}) }
- .to raise_error(Gitlab::PrometheusError, "Network connection error")
+ .to raise_error(Gitlab::PrometheusClient::Error, "Network connection error")
expect(req_stub).to have_been_requested
end
end
diff --git a/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb b/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb
index b49bc5c328c..f8faeffb935 100644
--- a/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb
+++ b/spec/lib/gitlab/query_limiting/active_support_subscriber_spec.rb
@@ -1,19 +1,34 @@
require 'spec_helper'
describe Gitlab::QueryLimiting::ActiveSupportSubscriber do
+ let(:transaction) { instance_double(Gitlab::QueryLimiting::Transaction, increment: true) }
+
+ before do
+ allow(Gitlab::QueryLimiting::Transaction)
+ .to receive(:current)
+ .and_return(transaction)
+ end
+
describe '#sql' do
it 'increments the number of executed SQL queries' do
- transaction = double(:transaction)
-
- allow(Gitlab::QueryLimiting::Transaction)
- .to receive(:current)
- .and_return(transaction)
+ User.count
expect(transaction)
- .to receive(:increment)
- .at_least(:once)
+ .to have_received(:increment)
+ .once
+ end
- User.count
+ context 'when the query is actually a rails cache hit' do
+ it 'does not increment the number of executed SQL queries' do
+ ActiveRecord::Base.connection.cache do
+ User.count
+ User.count
+ end
+
+ expect(transaction)
+ .to have_received(:increment)
+ .once
+ end
end
end
end
diff --git a/spec/lib/gitlab/query_limiting/transaction_spec.rb b/spec/lib/gitlab/query_limiting/transaction_spec.rb
index b4231fcd0fa..b72b8574174 100644
--- a/spec/lib/gitlab/query_limiting/transaction_spec.rb
+++ b/spec/lib/gitlab/query_limiting/transaction_spec.rb
@@ -59,18 +59,6 @@ describe Gitlab::QueryLimiting::Transaction do
expect { transaction.act_upon_results }
.to raise_error(described_class::ThresholdExceededError)
end
-
- it 'reports the error in Sentry if raising an error is disabled' do
- expect(transaction)
- .to receive(:raise_error?)
- .and_return(false)
-
- expect(Raven)
- .to receive(:capture_exception)
- .with(an_instance_of(described_class::ThresholdExceededError))
-
- transaction.act_upon_results
- end
end
end
diff --git a/spec/lib/gitlab/query_limiting_spec.rb b/spec/lib/gitlab/query_limiting_spec.rb
index 2eddab0b8c3..42877b1e2dd 100644
--- a/spec/lib/gitlab/query_limiting_spec.rb
+++ b/spec/lib/gitlab/query_limiting_spec.rb
@@ -12,14 +12,16 @@ describe Gitlab::QueryLimiting do
expect(described_class.enable?).to eq(true)
end
- it 'returns true on GitLab.com' do
+ it 'returns false on GitLab.com' do
+ expect(Rails.env).to receive(:development?).and_return(false)
+ expect(Rails.env).to receive(:test?).and_return(false)
allow(Gitlab).to receive(:com?).and_return(true)
- expect(described_class.enable?).to eq(true)
+ expect(described_class.enable?).to eq(false)
end
- it 'returns true in a non GitLab.com' do
- expect(Gitlab).to receive(:com?).and_return(false)
+ it 'returns false in a non GitLab.com' do
+ allow(Gitlab).to receive(:com?).and_return(false)
expect(Rails.env).to receive(:development?).and_return(false)
expect(Rails.env).to receive(:test?).and_return(false)
diff --git a/spec/lib/gitlab/quick_actions/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
index f44a562dc63..b03c1e23ca3 100644
--- a/spec/lib/gitlab/quick_actions/command_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
@@ -40,7 +40,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
describe "#available?" do
- let(:opts) { { go: false } }
+ let(:opts) { OpenStruct.new(go: false) }
context "when the command has a condition block" do
before do
@@ -78,7 +78,7 @@ describe Gitlab::QuickActions::CommandDefinition do
it "doesn't execute the command" do
expect(context).not_to receive(:instance_exec)
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be false
end
@@ -95,7 +95,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it "doesn't execute the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be false
end
@@ -109,7 +109,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is provided an argument" do
it "executes the command" do
- subject.execute(context, {}, true)
+ subject.execute(context, true)
expect(context.run).to be true
end
@@ -117,7 +117,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is not provided an argument" do
it "executes the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be true
end
@@ -131,7 +131,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is provided an argument" do
it "executes the command" do
- subject.execute(context, {}, true)
+ subject.execute(context, true)
expect(context.run).to be true
end
@@ -139,7 +139,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is not provided an argument" do
it "doesn't execute the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be false
end
@@ -153,7 +153,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is provided an argument" do
it "executes the command" do
- subject.execute(context, {}, true)
+ subject.execute(context, true)
expect(context.run).to be true
end
@@ -161,7 +161,7 @@ describe Gitlab::QuickActions::CommandDefinition do
context "when the command is not provided an argument" do
it "executes the command" do
- subject.execute(context, {}, nil)
+ subject.execute(context, nil)
expect(context.run).to be true
end
@@ -175,7 +175,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'executes the command passing the parsed param' do
- subject.execute(context, {}, 'something ')
+ subject.execute(context, 'something ')
expect(context.received_arg).to eq('something')
end
@@ -192,7 +192,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'returns nil' do
- result = subject.explain({}, {}, nil)
+ result = subject.explain({}, nil)
expect(result).to be_nil
end
@@ -204,7 +204,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'returns this static string' do
- result = subject.explain({}, {}, nil)
+ result = subject.explain({}, nil)
expect(result).to eq 'Explanation'
end
@@ -216,7 +216,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
it 'invokes the proc' do
- result = subject.explain({}, {}, 'explanation')
+ result = subject.explain({}, 'explanation')
expect(result).to eq 'Dynamic explanation'
end
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index ff59dc48bcb..067a30fd7e2 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -76,7 +76,7 @@ describe Gitlab::QuickActions::Dsl do
expect(dynamic_description_def.name).to eq(:dynamic_description)
expect(dynamic_description_def.aliases).to eq([])
- expect(dynamic_description_def.to_h(noteable: 'issue')[:description]).to eq('A dynamic description for ISSUE')
+ expect(dynamic_description_def.to_h(OpenStruct.new(noteable: 'issue'))[:description]).to eq('A dynamic description for ISSUE')
expect(dynamic_description_def.explanation).to eq('')
expect(dynamic_description_def.params).to eq(['The first argument', 'The second argument'])
expect(dynamic_description_def.condition_block).to be_nil
diff --git a/spec/lib/gitlab/regex_spec.rb b/spec/lib/gitlab/regex_spec.rb
index 8b54d72d6f7..a1079e54975 100644
--- a/spec/lib/gitlab/regex_spec.rb
+++ b/spec/lib/gitlab/regex_spec.rb
@@ -18,6 +18,7 @@ describe Gitlab::Regex do
subject { described_class.environment_name_regex }
it { is_expected.to match('foo') }
+ it { is_expected.to match('a') }
it { is_expected.to match('foo-1') }
it { is_expected.to match('FOO') }
it { is_expected.to match('foo/1') }
@@ -25,6 +26,10 @@ describe Gitlab::Regex do
it { is_expected.not_to match('9&foo') }
it { is_expected.not_to match('foo-^') }
it { is_expected.not_to match('!!()()') }
+ it { is_expected.not_to match('/foo') }
+ it { is_expected.not_to match('foo/') }
+ it { is_expected.not_to match('/foo/') }
+ it { is_expected.not_to match('/') }
end
describe '.environment_slug_regex' do
diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb
index 1a925a15e0c..b67bcc77bd4 100644
--- a/spec/lib/gitlab/repo_path_spec.rb
+++ b/spec/lib/gitlab/repo_path_spec.rb
@@ -6,11 +6,11 @@ describe ::Gitlab::RepoPath do
context 'a repository storage path' do
it 'parses a full repository path' do
- expect(described_class.parse(project.repository.path)).to eq([project, false, nil])
+ expect(described_class.parse(project.repository.full_path)).to eq([project, false, nil])
end
it 'parses a full wiki path' do
- expect(described_class.parse(project.wiki.repository.path)).to eq([project, true, nil])
+ expect(described_class.parse(project.wiki.repository.full_path)).to eq([project, true, nil])
end
end
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 2b61ce38418..4506cbc3982 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -443,7 +443,7 @@ describe Gitlab::Shell do
end
describe '#remove_repository' do
- let!(:project) { create(:project, :repository) }
+ let!(:project) { create(:project, :repository, :legacy_storage) }
let(:disk_path) { "#{project.disk_path}.git" }
it 'returns true when the command succeeds' do
diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
deleted file mode 100644
index 8fdbbacd04d..00000000000
--- a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-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 STP, TERM and KILL signals at expected times' do
- expect(subject).to receive(:sleep).with(15 * 60).ordered
- expect(Process).to receive(:kill).with('SIGSTP', 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
- 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
new file mode 100644
index 00000000000..0001795c3f0
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb
@@ -0,0 +1,88 @@
+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/slash_commands/command_spec.rb b/spec/lib/gitlab/slash_commands/command_spec.rb
index 0173a45d480..e3447d974aa 100644
--- a/spec/lib/gitlab/slash_commands/command_spec.rb
+++ b/spec/lib/gitlab/slash_commands/command_spec.rb
@@ -3,10 +3,11 @@ require 'spec_helper'
describe Gitlab::SlashCommands::Command do
let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:chat_name) { double(:chat_name, user: user) }
describe '#execute' do
subject do
- described_class.new(project, user, params).execute
+ described_class.new(project, chat_name, params).execute
end
context 'when no command is available' do
@@ -88,7 +89,7 @@ describe Gitlab::SlashCommands::Command do
end
describe '#match_command' do
- subject { described_class.new(project, user, params).match_command.first }
+ subject { described_class.new(project, chat_name, params).match_command.first }
context 'IssueShow is triggered' do
let(:params) { { text: 'issue show 123' } }
diff --git a/spec/lib/gitlab/slash_commands/deploy_spec.rb b/spec/lib/gitlab/slash_commands/deploy_spec.rb
index 74b5ef4bb26..0d57334aa4c 100644
--- a/spec/lib/gitlab/slash_commands/deploy_spec.rb
+++ b/spec/lib/gitlab/slash_commands/deploy_spec.rb
@@ -4,6 +4,7 @@ describe Gitlab::SlashCommands::Deploy do
describe '#execute' do
let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match('deploy staging to production') }
before do
@@ -16,7 +17,7 @@ describe Gitlab::SlashCommands::Deploy do
end
subject do
- described_class.new(project, user).execute(regex_match)
+ described_class.new(project, chat_name).execute(regex_match)
end
context 'if no environment is defined' do
diff --git a/spec/lib/gitlab/slash_commands/issue_new_spec.rb b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
index 3b077c58c50..8e7df946529 100644
--- a/spec/lib/gitlab/slash_commands/issue_new_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_new_spec.rb
@@ -4,6 +4,7 @@ describe Gitlab::SlashCommands::IssueNew do
describe '#execute' do
let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match("issue create bird is the word") }
before do
@@ -11,7 +12,7 @@ describe Gitlab::SlashCommands::IssueNew do
end
subject do
- described_class.new(project, user).execute(regex_match)
+ described_class.new(project, chat_name).execute(regex_match)
end
context 'without description' do
diff --git a/spec/lib/gitlab/slash_commands/issue_search_spec.rb b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
index 35d01efc1bd..189e9592f1b 100644
--- a/spec/lib/gitlab/slash_commands/issue_search_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_search_spec.rb
@@ -6,10 +6,11 @@ describe Gitlab::SlashCommands::IssueSearch do
let!(:confidential) { create(:issue, :confidential, project: project, title: 'mepmep find') }
let(:project) { create(:project) }
let(:user) { create(:user) }
+ let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match("issue search find") }
subject do
- described_class.new(project, user).execute(regex_match)
+ described_class.new(project, chat_name).execute(regex_match)
end
context 'when the user has no access' do
diff --git a/spec/lib/gitlab/slash_commands/issue_show_spec.rb b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
index e5834d5a2ee..b1db1638237 100644
--- a/spec/lib/gitlab/slash_commands/issue_show_spec.rb
+++ b/spec/lib/gitlab/slash_commands/issue_show_spec.rb
@@ -5,6 +5,7 @@ describe Gitlab::SlashCommands::IssueShow do
let(:issue) { create(:issue, project: project) }
let(:project) { create(:project) }
let(:user) { issue.author }
+ let(:chat_name) { double(:chat_name, user: user) }
let(:regex_match) { described_class.match("issue show #{issue.iid}") }
before do
@@ -12,7 +13,7 @@ describe Gitlab::SlashCommands::IssueShow do
end
subject do
- described_class.new(project, user).execute(regex_match)
+ described_class.new(project, chat_name).execute(regex_match)
end
context 'the issue exists' do
diff --git a/spec/lib/gitlab/sql/pattern_spec.rb b/spec/lib/gitlab/sql/pattern_spec.rb
index ef51e3cc8df..5b5052de372 100644
--- a/spec/lib/gitlab/sql/pattern_spec.rb
+++ b/spec/lib/gitlab/sql/pattern_spec.rb
@@ -154,6 +154,12 @@ describe Gitlab::SQL::Pattern do
it 'returns a single equality condition' do
expect(fuzzy_arel_match.to_sql).to match(/title.*I?LIKE 'fo'/)
end
+
+ it 'uses LOWER instead of ILIKE when LOWER is enabled' do
+ rel = Issue.fuzzy_arel_match(:title, query, lower_exact_match: true)
+
+ expect(rel.to_sql).to match(/LOWER\(.*title.*\).*=.*'fo'/)
+ end
end
context 'with two words both equal to 3 chars' do
diff --git a/spec/lib/gitlab/ssh_public_key_spec.rb b/spec/lib/gitlab/ssh_public_key_spec.rb
index c15e29774b6..a6ea07e8b6d 100644
--- a/spec/lib/gitlab/ssh_public_key_spec.rb
+++ b/spec/lib/gitlab/ssh_public_key_spec.rb
@@ -76,7 +76,21 @@ describe Gitlab::SSHPublicKey, lib: true do
subject { public_key }
context 'with a valid SSH key' do
- it { is_expected.to be_valid }
+ where(:factory) do
+ %i(rsa_key_2048
+ rsa_key_4096
+ rsa_key_5120
+ rsa_key_8192
+ dsa_key_2048
+ ecdsa_key_256
+ ed25519_key_256)
+ end
+
+ with_them do
+ let(:key) { attributes_for(factory)[:key] }
+
+ it { is_expected.to be_valid }
+ end
end
context 'with an invalid SSH key' do
@@ -117,6 +131,9 @@ describe Gitlab::SSHPublicKey, lib: true do
where(:factory, :bits) do
[
[:rsa_key_2048, 2048],
+ [:rsa_key_4096, 4096],
+ [:rsa_key_5120, 5120],
+ [:rsa_key_8192, 8192],
[:dsa_key_2048, 2048],
[:ecdsa_key_256, 256],
[:ed25519_key_256, 256]
@@ -141,8 +158,11 @@ describe Gitlab::SSHPublicKey, lib: true do
where(:factory, :fingerprint) do
[
- [:rsa_key_2048, '2e:ca:dc:e0:37:29:ed:fc:f0:1d:bf:66:d4:cd:51:b1'],
- [:dsa_key_2048, 'bc:c1:a4:be:7e:8c:84:56:b3:58:93:53:c6:80:78:8c'],
+ [:rsa_key_2048, '58:a8:9d:cd:1f:70:f8:5a:d9:e4:24:8e:da:89:e4:fc'],
+ [:rsa_key_4096, 'df:73:db:29:3c:a5:32:cf:09:17:7e:8e:9d:de:d7:f7'],
+ [:rsa_key_5120, 'fe:fa:3a:4d:7d:51:ec:bf:c7:64:0c:96:d0:17:8a:d0'],
+ [:rsa_key_8192, 'fb:53:7f:e9:2f:f7:17:aa:c8:32:52:06:8e:05:e2:82'],
+ [:dsa_key_2048, 'c8:85:1e:df:44:0f:20:00:3c:66:57:2b:21:10:5a:27'],
[:ecdsa_key_256, '67:a3:a9:7d:b8:e1:15:d4:80:40:21:34:bb:ed:97:38'],
[:ed25519_key_256, 'e6:eb:45:8a:3c:59:35:5f:e9:5b:80:12:be:7e:22:73']
]
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index dc2bb5b9747..37a0bf1ad36 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -324,7 +324,7 @@ describe Gitlab::Workhorse do
it 'includes a Repository param' do
repo_param = {
storage_name: 'default',
- relative_path: project.full_path + '.git',
+ relative_path: project.disk_path + '.git',
gl_repository: "project-#{project.id}"
}
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index f65e41dfea3..db9d9158b29 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -115,6 +115,9 @@ describe GoogleApi::CloudPlatform::Client do
"initial_node_count": cluster_size,
"node_config": {
"machine_type": machine_type
+ },
+ "legacy_abac": {
+ "enabled": true
}
}
} )
diff --git a/spec/mailers/emails/pages_domains_spec.rb b/spec/mailers/emails/pages_domains_spec.rb
new file mode 100644
index 00000000000..fe428ea657d
--- /dev/null
+++ b/spec/mailers/emails/pages_domains_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+require 'email_spec'
+
+describe Emails::PagesDomains do
+ include EmailSpec::Matchers
+ include_context 'gitlab email notification'
+
+ set(:project) { create(:project) }
+ set(:domain) { create(:pages_domain, project: project) }
+ set(:user) { project.owner }
+
+ shared_examples 'a pages domain email' 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 'has the expected content' do
+ aggregate_failures do
+ is_expected.to have_subject(email_subject)
+ is_expected.to have_body_text(project.human_name)
+ is_expected.to have_body_text(domain.domain)
+ is_expected.to have_body_text domain.url
+ is_expected.to have_body_text project_pages_domain_url(project, domain)
+ is_expected.to have_body_text help_page_url('user/project/pages/getting_started_part_three.md', anchor: 'dns-txt-record')
+ end
+ end
+ end
+
+ describe '#pages_domain_enabled_email' do
+ let(:email_subject) { "#{project.path} | GitLab Pages domain '#{domain.domain}' has been enabled" }
+
+ subject { Notify.pages_domain_enabled_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'has been enabled' }
+ end
+
+ describe '#pages_domain_disabled_email' do
+ let(:email_subject) { "#{project.path} | GitLab Pages domain '#{domain.domain}' has been disabled" }
+
+ subject { Notify.pages_domain_disabled_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'has been disabled' }
+ end
+
+ describe '#pages_domain_verification_succeeded_email' do
+ let(:email_subject) { "#{project.path} | Verification succeeded for GitLab Pages domain '#{domain.domain}'" }
+
+ subject { Notify.pages_domain_verification_succeeded_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it { is_expected.to have_body_text 'successfully verified' }
+ end
+
+ describe '#pages_domain_verification_failed_email' do
+ let(:email_subject) { "#{project.path} | ACTION REQUIRED: Verification failed for GitLab Pages domain '#{domain.domain}'" }
+
+ subject { Notify.pages_domain_verification_failed_email(domain, user) }
+
+ it_behaves_like 'a pages domain email'
+
+ it 'says verification has failed and when the domain is enabled until' do
+ is_expected.to have_body_text 'Verification has failed'
+ is_expected.to have_body_text domain.enabled_until.strftime('%F %T')
+ end
+ end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 59eda025108..bcbb9287199 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -463,59 +463,30 @@ describe Notify do
end
describe 'project access requested' do
- context 'for a project in a user namespace' do
- let(:project) do
- create(:project, :public, :access_requestable) do |project|
- project.add_master(project.owner, current_user: project.owner)
- end
- end
-
- let(:project_member) do
- project.request_access(user)
- project.requesters.find_by(user_id: user.id)
- end
- subject { described_class.member_access_requested_email('project', project_member.id) }
-
- 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 'contains all the useful information' do
- to_emails = subject.header[:to].addrs
- expect(to_emails.size).to eq(1)
- expect(to_emails[0].address).to eq(project.members.owners_and_masters.first.user.notification_email)
-
- is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
- is_expected.to have_html_escaped_body_text project.name_with_namespace
- is_expected.to have_body_text project_project_members_url(project)
- is_expected.to have_body_text project_member.human_access
+ let(:project) do
+ create(:project, :public, :access_requestable) do |project|
+ project.add_master(project.owner)
end
end
- context 'for a project in a group' do
- let(:group_owner) { create(:user) }
- let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } }
- let(:project) { create(:project, :public, :access_requestable, namespace: group) }
- let(:project_member) do
- project.request_access(user)
- project.requesters.find_by(user_id: user.id)
- end
- subject { described_class.member_access_requested_email('project', project_member.id) }
+ let(:project_member) do
+ project.request_access(user)
+ project.requesters.find_by(user_id: user.id)
+ end
+ subject { described_class.member_access_requested_email('project', project_member.id, recipient.notification_email) }
- 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 '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 'contains all the useful information' do
- to_emails = subject.header[:to].addrs
- expect(to_emails.size).to eq(1)
- expect(to_emails[0].address).to eq(group.members.owners_and_masters.first.user.notification_email)
+ it 'contains all the useful information' do
+ to_emails = subject.header[:to].addrs.map(&:address)
+ expect(to_emails).to eq([recipient.notification_email])
- is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
- is_expected.to have_html_escaped_body_text project.name_with_namespace
- is_expected.to have_body_text project_project_members_url(project)
- is_expected.to have_body_text project_member.human_access
- end
+ is_expected.to have_subject "Request to join the #{project.name_with_namespace} project"
+ is_expected.to have_html_escaped_body_text project.name_with_namespace
+ is_expected.to have_body_text project_project_members_url(project)
+ is_expected.to have_body_text project_member.human_access
end
end
@@ -959,13 +930,16 @@ describe Notify do
group.request_access(user)
group.requesters.find_by(user_id: user.id)
end
- subject { described_class.member_access_requested_email('group', group_member.id) }
+ subject { described_class.member_access_requested_email('group', group_member.id, recipient.notification_email) }
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 'contains all the useful information' do
+ to_emails = subject.header[:to].addrs.map(&:address)
+ expect(to_emails).to eq([recipient.notification_email])
+
is_expected.to have_subject "Request to join the #{group.name} group"
is_expected.to have_html_escaped_body_text group.name
is_expected.to have_body_text group_group_members_url(group)
diff --git a/spec/migrations/README.md b/spec/migrations/README.md
index 45cf25b96de..49760fa62b8 100644
--- a/spec/migrations/README.md
+++ b/spec/migrations/README.md
@@ -89,5 +89,5 @@ end
## Best practices
1. Note that this type of tests do not run within the transaction, we use
-a truncation database cleanup strategy. Do not depend on transaction being
+a deletion database cleanup strategy. Do not depend on transaction being
present.
diff --git a/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
new file mode 100644
index 00000000000..afcaefa0591
--- /dev/null
+++ b/spec/migrations/enqueue_verify_pages_domain_workers_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180216121030_enqueue_verify_pages_domain_workers')
+
+describe EnqueueVerifyPagesDomainWorkers, :sidekiq, :migration do
+ around do |example|
+ Sidekiq::Testing.fake! do
+ example.run
+ end
+ end
+
+ describe '#up' do
+ it 'enqueues a verification worker for every domain' do
+ domains = 1.upto(3).map { |i| PagesDomain.create!(domain: "my#{i}.domain.com") }
+
+ expect { migrate! }.to change(PagesDomainVerificationWorker.jobs, :size).by(3)
+
+ enqueued_ids = PagesDomainVerificationWorker.jobs.map { |job| job['args'] }
+ expected_ids = domains.map { |domain| [domain.id] }
+
+ expect(enqueued_ids).to match_array(expected_ids)
+ end
+ end
+end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index e5793a3c0ee..657113812bd 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_worker_jobs.rb')
describe MigrateProcessCommitWorkerJobs do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :legacy_storage, :repository) }
let(:user) { create(:user) }
let(:commit) { project.commit.raw.rugged_commit }
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
index e6555b1fe6b..34336d705b1 100644
--- a/spec/migrations/rename_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -3,10 +3,14 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_project_names.rb')
-# This migration uses multiple threads, and thus different transactions. This
-# means data created in this spec may not be visible to some threads. To work
-# around this we use the DELETE cleaning strategy.
-describe RenameReservedProjectNames, :delete do
+# This migration is using factories, which set fields that don't actually
+# exist in the DB schema previous to 20161221153951. Thus we just use the
+# latest schema when testing this migration.
+# This is ok-ish because:
+# 1. This migration is a data migration
+# 2. It only relies on very stable DB fields: routes.id, routes.path, namespaces.id, projects.namespace_id
+# Ideally, the test should not use factories and rely on the `table` helper instead.
+describe RenameReservedProjectNames, :migration, schema: :latest do
let(:migration) { described_class.new }
let!(:project) { create(:project) }
diff --git a/spec/migrations/track_untracked_uploads_spec.rb b/spec/migrations/track_untracked_uploads_spec.rb
index fe4d5b8a279..2fccfb3f12c 100644
--- a/spec/migrations/track_untracked_uploads_spec.rb
+++ b/spec/migrations/track_untracked_uploads_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20171103140253_track_untracked_uploads')
describe TrackUntrackedUploads, :migration, :sidekiq do
- include TrackUntrackedUploadsHelpers
+ include MigrationsHelpers::TrackUntrackedUploadsHelpers
it 'correctly schedules the follow-up background migration' do
Sidekiq::Testing.fake! do
diff --git a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
index 6f7a730edff..528dc54781d 100644
--- a/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
+++ b/spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb
@@ -4,7 +4,7 @@ require Rails.root.join('db', 'migrate', '20170503140202_turn_nested_groups_into
describe TurnNestedGroupsIntoRegularGroupsForMysql do
let!(:parent_group) { create(:group) }
let!(:child_group) { create(:group, parent: parent_group) }
- let!(:project) { create(:project, :empty_repo, namespace: child_group) }
+ let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: child_group) }
let!(:member) { create(:user) }
let(:migration) { described_class.new }
diff --git a/spec/models/ability_spec.rb b/spec/models/ability_spec.rb
index 38fb98d4f50..cd175dba6da 100644
--- a/spec/models/ability_spec.rb
+++ b/spec/models/ability_spec.rb
@@ -204,6 +204,78 @@ describe Ability do
end
end
+ describe '.merge_requests_readable_by_user' do
+ context 'with an admin' do
+ it 'returns all merge requests' do
+ user = build(:user, admin: true)
+ merge_request = build(:merge_request)
+
+ expect(described_class.merge_requests_readable_by_user([merge_request], user))
+ .to eq([merge_request])
+ end
+ end
+
+ context 'without a user' do
+ it 'returns merge_requests that are publicly visible' do
+ hidden_merge_request = build(:merge_request)
+ visible_merge_request = build(:merge_request, source_project: build(:project, :public))
+
+ merge_requests = described_class
+ .merge_requests_readable_by_user([hidden_merge_request, visible_merge_request])
+
+ expect(merge_requests).to eq([visible_merge_request])
+ end
+ end
+
+ context 'with a user' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:merge_request) { create(:merge_request, source_project: project) }
+ let(:cross_project_merge_request) do
+ create(:merge_request, source_project: create(:project, :public))
+ end
+ let(:other_merge_request) { create(:merge_request) }
+ let(:all_merge_requests) do
+ [merge_request, cross_project_merge_request, other_merge_request]
+ end
+
+ subject(:readable_merge_requests) do
+ described_class.merge_requests_readable_by_user(all_merge_requests, user)
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns projects visible to the user' do
+ expect(readable_merge_requests).to contain_exactly(merge_request, cross_project_merge_request)
+ end
+
+ context 'when a user cannot read cross project and a filter is passed' do
+ before do
+ allow(described_class).to receive(:allowed?).and_call_original
+ expect(described_class).to receive(:allowed?).with(user, :read_cross_project) { false }
+ end
+
+ subject(:readable_merge_requests) do
+ read_cross_project_filter = -> (merge_requests) do
+ merge_requests.select { |mr| mr.source_project == project }
+ end
+ described_class.merge_requests_readable_by_user(
+ all_merge_requests, user,
+ filters: { read_cross_project: read_cross_project_filter }
+ )
+ end
+
+ it 'returns only MRs of the specified project without checking access on others' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_merge_request, cross_project_merge_request)
+
+ expect(readable_merge_requests).to contain_exactly(merge_request)
+ end
+ end
+ end
+ end
+
describe '.issues_readable_by_user' do
context 'with an admin user' do
it 'returns all given issues' do
@@ -250,6 +322,29 @@ describe Ability do
expect(issues).to eq([visible_issue])
end
end
+
+ context 'when the user cannot read cross project' do
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue) }
+ let(:other_project_issue) { create(:issue) }
+ let(:project) { issue.project }
+
+ before do
+ project.add_developer(user)
+
+ allow(described_class).to receive(:allowed?).and_call_original
+ allow(described_class).to receive(:allowed?).with(user, :read_cross_project, any_args) { false }
+ end
+
+ it 'excludes issues from other projects whithout checking separatly when passing a scope' do
+ expect(described_class).not_to receive(:allowed?).with(user, :read_issue, other_project_issue)
+
+ filters = { read_cross_project: -> (issues) { issues.where(project: project) } }
+ result = described_class.issues_readable_by_user(Issue.all, user, filters: filters)
+
+ expect(result).to contain_exactly(issue)
+ end
+ end
end
describe '.project_disabled_features_rules' do
diff --git a/spec/models/chat_name_spec.rb b/spec/models/chat_name_spec.rb
index e89e534d914..504bc710b25 100644
--- a/spec/models/chat_name_spec.rb
+++ b/spec/models/chat_name_spec.rb
@@ -14,4 +14,24 @@ describe ChatName do
it { is_expected.to validate_uniqueness_of(:user_id).scoped_to(:service_id) }
it { is_expected.to validate_uniqueness_of(:chat_id).scoped_to(:service_id, :team_id) }
+
+ describe '#update_last_used_at', :clean_gitlab_redis_shared_state do
+ it 'updates the last_used_at timestamp' do
+ expect(subject.last_used_at).to be_nil
+
+ subject.update_last_used_at
+
+ expect(subject.last_used_at).to be_present
+ end
+
+ it 'does not update last_used_at if it was recently updated' do
+ subject.update_last_used_at
+
+ time = subject.last_used_at
+
+ subject.update_last_used_at
+
+ expect(subject.last_used_at).to eq(time)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 559e6f12565..917e6eec753 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -335,7 +335,7 @@ describe Ci::Build do
allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
end
- it { is_expected.to be_an(Array).and all(include(key: "key_1")) }
+ it { is_expected.to be_an(Array).and all(include(key: "key-1")) }
end
context 'when project does not have jobs_cache_index' do
@@ -1471,6 +1471,7 @@ describe Ci::Build do
[
{ key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true },
+ { key: 'GITLAB_FEATURES', value: project.namespace.features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
@@ -1647,7 +1648,7 @@ describe Ci::Build do
context 'when the branch is protected' do
before do
- create(:protected_branch, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -1655,7 +1656,7 @@ describe Ci::Build do
context 'when the tag is protected' do
before do
- create(:protected_tag, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -1692,7 +1693,7 @@ describe Ci::Build do
context 'when the branch is protected' do
before do
- create(:protected_branch, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
@@ -1700,7 +1701,7 @@ describe Ci::Build do
context 'when the tag is protected' do
before do
- create(:protected_tag, project: build.project, name: build.ref)
+ allow(build.project).to receive(:protected_for?).with(build.ref).and_return(true)
end
it { is_expected.to include(protected_variable) }
diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb
index 145189e7469..1b10501701c 100644
--- a/spec/models/ci/group_variable_spec.rb
+++ b/spec/models/ci/group_variable_spec.rb
@@ -5,7 +5,7 @@ describe Ci::GroupVariable do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to include_module(Presentable) }
- it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id).with_message(/\(\w+\) has already been taken/) }
describe '.unprotected' do
subject { described_class.unprotected }
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index e4ff551151e..875e8b2b682 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -6,7 +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 validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope) }
+ it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope).with_message(/\(\w+\) has already been taken/) }
end
describe '.unprotected' do
diff --git a/spec/models/concerns/protected_ref_access_spec.rb b/spec/models/concerns/protected_ref_access_spec.rb
new file mode 100644
index 00000000000..a62ca391e25
--- /dev/null
+++ b/spec/models/concerns/protected_ref_access_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe ProtectedRefAccess do
+ subject(:protected_ref_access) do
+ create(:protected_branch, :masters_can_push).push_access_levels.first
+ end
+
+ let(:project) { protected_ref_access.project }
+
+ describe '#check_access' do
+ it 'is always true for admins' do
+ admin = create(:admin)
+
+ expect(protected_ref_access.check_access(admin)).to be_truthy
+ end
+
+ it 'is true for masters' do
+ master = create(:user)
+ project.add_master(master)
+
+ expect(protected_ref_access.check_access(master)).to be_truthy
+ end
+
+ it 'is for developers of the project' do
+ developer = create(:user)
+ project.add_developer(developer)
+
+ expect(protected_ref_access.check_access(developer)).to be_falsy
+ end
+ end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 338fb314ee9..4f16b73ef38 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -549,7 +549,7 @@ describe Group do
context 'when the ref is a protected branch' do
before do
- create(:protected_branch, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -557,7 +557,7 @@ describe Group do
context 'when the ref is a protected tag' do
before do
- create(:protected_tag, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -571,6 +571,10 @@ describe Group do
let(:variable_child_2) { create(:ci_group_variable, group: group_child_2) }
let(:variable_child_3) { create(:ci_group_variable, group: group_child_3) }
+ before do
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
+ end
+
it 'returns all variables belong to the group and parent groups' do
expected_array1 = [protected_variable, secret_variable]
expected_array2 = [variable_child, variable_child_2, variable_child_3]
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
index 7c66c98231b..a5ce245c21d 100644
--- a/spec/models/identity_spec.rb
+++ b/spec/models/identity_spec.rb
@@ -70,5 +70,38 @@ describe Identity do
end
end
end
+
+ context 'after_destroy' do
+ let!(:user) { create(:user) }
+ let(:ldap_identity) { create(:identity, provider: 'ldapmain', extern_uid: 'uid=john smith,ou=people,dc=example,dc=com', user: user) }
+ let(:ldap_user_synced_attributes) { { provider: 'ldapmain', name_synced: true, email_synced: true } }
+ let(:other_provider_user_synced_attributes) { { provider: 'other', name_synced: true, email_synced: true } }
+
+ describe 'if user synced attributes metadada provider' do
+ context 'matches the identity provider ' do
+ it 'removes the user synced attributes' do
+ user.create_user_synced_attributes_metadata(ldap_user_synced_attributes)
+
+ expect(user.user_synced_attributes_metadata.provider).to eq 'ldapmain'
+
+ ldap_identity.destroy
+
+ expect(user.reload.user_synced_attributes_metadata).to be_nil
+ end
+ end
+
+ context 'does not matche the identity provider' do
+ it 'does not remove the user synced attributes' do
+ user.create_user_synced_attributes_metadata(other_provider_user_synced_attributes)
+
+ expect(user.user_synced_attributes_metadata.provider).to eq 'other'
+
+ ldap_identity.destroy
+
+ expect(user.reload.user_synced_attributes_metadata.provider).to eq 'other'
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index f5c9f551e65..feed7968f09 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -221,27 +221,55 @@ describe Issue do
end
describe '#referenced_merge_requests' do
- it 'returns the referenced merge requests' do
- project = create(:project, :public)
-
- mr1 = create(:merge_request,
- source_project: project,
- source_branch: 'master',
- target_branch: 'feature')
+ let(:project) { create(:project, :public) }
+ let(:issue) do
+ create(:issue, description: merge_request.to_reference, project: project)
+ end
+ let!(:merge_request) do
+ create(:merge_request,
+ source_project: project,
+ source_branch: 'master',
+ target_branch: 'feature')
+ end
+ it 'returns the referenced merge requests' do
mr2 = create(:merge_request,
source_project: project,
source_branch: 'feature',
target_branch: 'master')
- issue = create(:issue, description: mr1.to_reference, project: project)
-
create(:note_on_issue,
noteable: issue,
note: mr2.to_reference,
project_id: project.id)
- expect(issue.referenced_merge_requests).to eq([mr1, mr2])
+ expect(issue.referenced_merge_requests).to eq([merge_request, mr2])
+ end
+
+ it 'returns cross project referenced merge requests' do
+ other_project = create(:project, :public)
+ cross_project_merge_request = create(:merge_request, source_project: other_project)
+ create(:note_on_issue,
+ noteable: issue,
+ note: cross_project_merge_request.to_reference(issue.project),
+ project_id: issue.project.id)
+
+ expect(issue.referenced_merge_requests).to eq([merge_request, cross_project_merge_request])
+ end
+
+ it 'excludes cross project references if the user cannot read cross project' do
+ user = create(:user)
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ other_project = create(:project, :public)
+ cross_project_merge_request = create(:merge_request, source_project: other_project)
+ create(:note_on_issue,
+ noteable: issue,
+ note: cross_project_merge_request.to_reference(issue.project),
+ project_id: issue.project.id)
+
+ expect(issue.referenced_merge_requests(user)).to eq([merge_request])
end
end
@@ -309,7 +337,7 @@ describe Issue do
end
describe '#related_branches' do
- let(:user) { build(:admin) }
+ let(:user) { create(:admin) }
before do
allow(subject.project.repository).to receive(:branch_names)
diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb
index bf5703ac986..06d26ef89f1 100644
--- a/spec/models/key_spec.rb
+++ b/spec/models/key_spec.rb
@@ -12,6 +12,9 @@ describe Key, :mailer do
it { is_expected.to validate_presence_of(:key) }
it { is_expected.to validate_length_of(:key).is_at_most(5000) }
it { is_expected.to allow_value(attributes_for(:rsa_key_2048)[:key]).for(:key) }
+ it { is_expected.to allow_value(attributes_for(:rsa_key_4096)[:key]).for(:key) }
+ it { is_expected.to allow_value(attributes_for(:rsa_key_5120)[:key]).for(:key) }
+ it { is_expected.to allow_value(attributes_for(:rsa_key_8192)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:dsa_key_2048)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:ecdsa_key_256)[:key]).for(:key) }
it { is_expected.to allow_value(attributes_for(:ed25519_key_256)[:key]).for(:key) }
@@ -103,24 +106,6 @@ describe Key, :mailer do
end
end
- context 'validate size' do
- where(:key_content, :result) do
- [
- [Spec::Support::Helpers::KeyGeneratorHelper.new(512).generate, false],
- [Spec::Support::Helpers::KeyGeneratorHelper.new(8192).generate, false],
- [Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate, true]
- ]
- end
-
- with_them do
- it 'validates the size of the key' do
- key = build(:key, key: key_content)
-
- expect(key.valid?).to eq(result)
- end
- end
- end
-
context 'validate it meets key restrictions' do
where(:factory, :minimum, :result) do
forbidden = ApplicationSetting::FORBIDDEN_KEY_VALUE
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 191b60e4383..e626efd054d 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -168,84 +168,105 @@ describe Namespace do
end
describe '#move_dir', :request_store do
- let(:namespace) { create(:namespace) }
- let!(:project) { create(:project_empty_repo, namespace: namespace) }
+ shared_examples "namespace restrictions" do
+ context "when any project has container images" do
+ let(:container_repository) { create(:container_repository) }
- it "raises error when directory exists" do
- expect { namespace.move_dir }.to raise_error("namespace directory cannot be moved")
- end
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags(repository: :any, tags: ['tag'])
- it "moves dir if path changed" do
- namespace.update_attributes(path: namespace.full_path + '_new')
+ create(:project, namespace: namespace, container_repositories: [container_repository])
- expect(gitlab_shell.exists?(project.repository_storage_path, "#{namespace.path}/#{project.path}.git")).to be_truthy
- end
+ allow(namespace).to receive(:path_was).and_return(namespace.path)
+ allow(namespace).to receive(:path).and_return('new_path')
+ end
- context "when any project has container images" do
- let(:container_repository) { create(:container_repository) }
+ it 'raises an error about not movable project' do
+ expect { namespace.move_dir }.to raise_error(/Namespace cannot be moved/)
+ end
+ end
+ end
- before do
- stub_container_registry_config(enabled: true)
- stub_container_registry_tags(repository: :any, tags: ['tag'])
+ context 'legacy storage' do
+ let(:namespace) { create(:namespace) }
+ let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: namespace) }
- create(:project, namespace: namespace, container_repositories: [container_repository])
+ it_behaves_like 'namespace restrictions'
- allow(namespace).to receive(:path_was).and_return(namespace.path)
- allow(namespace).to receive(:path).and_return('new_path')
+ it "raises error when directory exists" do
+ expect { namespace.move_dir }.to raise_error("namespace directory cannot be moved")
end
- it 'raises an error about not movable project' do
- expect { namespace.move_dir }.to raise_error(/Namespace cannot be moved/)
+ it "moves dir if path changed" do
+ namespace.update_attributes(path: namespace.full_path + '_new')
+
+ expect(gitlab_shell.exists?(project.repository_storage_path, "#{namespace.path}/#{project.path}.git")).to be_truthy
end
- end
- context 'with subgroups' do
- let(:parent) { create(:group, name: 'parent', path: 'parent') }
- let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
- let!(:project) { create(:project_empty_repo, path: 'the-project', namespace: child, skip_disk_validation: true) }
- let(:uploads_dir) { FileUploader.root }
- let(:pages_dir) { File.join(TestEnv.pages_path) }
+ context 'with subgroups' do
+ let(:parent) { create(:group, name: 'parent', path: 'parent') }
+ let(:child) { create(:group, name: 'child', path: 'child', parent: parent) }
+ let!(:project) { create(:project_empty_repo, :legacy_storage, path: 'the-project', namespace: child, skip_disk_validation: true) }
+ let(:uploads_dir) { FileUploader.root }
+ let(:pages_dir) { File.join(TestEnv.pages_path) }
- before do
- FileUtils.mkdir_p(File.join(uploads_dir, 'parent', 'child', 'the-project'))
- FileUtils.mkdir_p(File.join(pages_dir, 'parent', 'child', 'the-project'))
- end
+ before do
+ FileUtils.mkdir_p(File.join(uploads_dir, project.full_path))
+ FileUtils.mkdir_p(File.join(pages_dir, project.full_path))
+ end
+
+ context 'renaming child' do
+ it 'correctly moves the repository, uploads and pages' do
+ expected_repository_path = File.join(TestEnv.repos_path, 'parent', 'renamed', 'the-project.git')
+ expected_upload_path = File.join(uploads_dir, 'parent', 'renamed', 'the-project')
+ expected_pages_path = File.join(pages_dir, 'parent', 'renamed', 'the-project')
- context 'renaming child' do
- it 'correctly moves the repository, uploads and pages' do
- expected_repository_path = File.join(TestEnv.repos_path, 'parent', 'renamed', 'the-project.git')
- expected_upload_path = File.join(uploads_dir, 'parent', 'renamed', 'the-project')
- expected_pages_path = File.join(pages_dir, 'parent', 'renamed', 'the-project')
+ child.update_attributes!(path: 'renamed')
- child.update_attributes!(path: 'renamed')
+ expect(File.directory?(expected_repository_path)).to be(true)
+ expect(File.directory?(expected_upload_path)).to be(true)
+ expect(File.directory?(expected_pages_path)).to be(true)
+ end
+ end
+
+ context 'renaming parent' do
+ it 'correctly moves the repository, uploads and pages' do
+ expected_repository_path = File.join(TestEnv.repos_path, 'renamed', 'child', 'the-project.git')
+ expected_upload_path = File.join(uploads_dir, 'renamed', 'child', 'the-project')
+ expected_pages_path = File.join(pages_dir, 'renamed', 'child', 'the-project')
- expect(File.directory?(expected_repository_path)).to be(true)
- expect(File.directory?(expected_upload_path)).to be(true)
- expect(File.directory?(expected_pages_path)).to be(true)
+ parent.update_attributes!(path: 'renamed')
+
+ expect(File.directory?(expected_repository_path)).to be(true)
+ expect(File.directory?(expected_upload_path)).to be(true)
+ expect(File.directory?(expected_pages_path)).to be(true)
+ end
end
end
+ end
- context 'renaming parent' do
- it 'correctly moves the repository, uploads and pages' do
- expected_repository_path = File.join(TestEnv.repos_path, 'renamed', 'child', 'the-project.git')
- expected_upload_path = File.join(uploads_dir, 'renamed', 'child', 'the-project')
- expected_pages_path = File.join(pages_dir, 'renamed', 'child', 'the-project')
+ context 'hashed storage' do
+ let(:namespace) { create(:namespace) }
+ let!(:project) { create(:project_empty_repo, namespace: namespace) }
- parent.update_attributes!(path: 'renamed')
+ it_behaves_like 'namespace restrictions'
- expect(File.directory?(expected_repository_path)).to be(true)
- expect(File.directory?(expected_upload_path)).to be(true)
- expect(File.directory?(expected_pages_path)).to be(true)
- end
+ it "repository directory remains unchanged if path changed" do
+ before_disk_path = project.disk_path
+ namespace.update_attributes(path: namespace.full_path + '_new')
+
+ expect(before_disk_path).to eq(project.disk_path)
+ expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_truthy
end
end
it 'updates project full path in .git/config for each project inside namespace' do
parent = create(:group, name: 'mygroup', path: 'mygroup')
subgroup = create(:group, name: 'mysubgroup', path: 'mysubgroup', parent: parent)
- project_in_parent_group = create(:project, :repository, namespace: parent, name: 'foo1')
- hashed_project_in_subgroup = create(:project, :repository, :hashed, namespace: subgroup, name: 'foo2')
- legacy_project_in_subgroup = create(:project, :repository, namespace: subgroup, name: 'foo3')
+ project_in_parent_group = create(:project, :legacy_storage, :repository, namespace: parent, name: 'foo1')
+ hashed_project_in_subgroup = create(:project, :repository, namespace: subgroup, name: 'foo2')
+ legacy_project_in_subgroup = create(:project, :legacy_storage, :repository, namespace: subgroup, name: 'foo3')
parent.update(path: 'mygroup_new')
@@ -260,38 +281,18 @@ describe Namespace do
end
describe '#rm_dir', 'callback' do
- let!(:project) { create(:project_empty_repo, namespace: namespace) }
let(:repository_storage_path) { Gitlab.config.repositories.storages.default['path'] }
let(:path_in_dir) { File.join(repository_storage_path, namespace.full_path) }
let(:deleted_path) { namespace.full_path.gsub(namespace.path, "#{namespace.full_path}+#{namespace.id}+deleted") }
let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) }
- it 'renames its dirs when deleted' do
- allow(GitlabShellWorker).to receive(:perform_in)
-
- namespace.destroy
-
- expect(File.exist?(deleted_path_in_dir)).to be(true)
- end
-
- it 'schedules the namespace for deletion' do
- expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage_path, deleted_path)
-
- namespace.destroy
- end
-
- context 'in sub-groups' do
- let(:parent) { create(:group, path: 'parent') }
- let(:child) { create(:group, parent: parent, path: 'child') }
- let!(:project) { create(:project_empty_repo, namespace: child) }
- let(:path_in_dir) { File.join(repository_storage_path, 'parent', 'child') }
- let(:deleted_path) { File.join('parent', "child+#{child.id}+deleted") }
- let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) }
+ context 'legacy storage' do
+ let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: namespace) }
it 'renames its dirs when deleted' do
allow(GitlabShellWorker).to receive(:perform_in)
- child.destroy
+ namespace.destroy
expect(File.exist?(deleted_path_in_dir)).to be(true)
end
@@ -299,14 +300,57 @@ describe Namespace do
it 'schedules the namespace for deletion' do
expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage_path, deleted_path)
- child.destroy
+ namespace.destroy
+ end
+
+ context 'in sub-groups' do
+ let(:parent) { create(:group, path: 'parent') }
+ let(:child) { create(:group, parent: parent, path: 'child') }
+ let!(:project) { create(:project_empty_repo, :legacy_storage, namespace: child) }
+ let(:path_in_dir) { File.join(repository_storage_path, 'parent', 'child') }
+ let(:deleted_path) { File.join('parent', "child+#{child.id}+deleted") }
+ let(:deleted_path_in_dir) { File.join(repository_storage_path, deleted_path) }
+
+ it 'renames its dirs when deleted' do
+ allow(GitlabShellWorker).to receive(:perform_in)
+
+ child.destroy
+
+ expect(File.exist?(deleted_path_in_dir)).to be(true)
+ end
+
+ it 'schedules the namespace for deletion' do
+ expect(GitlabShellWorker).to receive(:perform_in).with(5.minutes, :rm_namespace, repository_storage_path, deleted_path)
+
+ child.destroy
+ end
+ end
+
+ it 'removes the exports folder' do
+ expect(namespace).to receive(:remove_exports!)
+
+ namespace.destroy
end
end
- it 'removes the exports folder' do
- expect(namespace).to receive(:remove_exports!)
+ context 'hashed storage' do
+ let!(:project) { create(:project_empty_repo, namespace: namespace) }
+
+ it 'has no repositories base directories to remove' do
+ allow(GitlabShellWorker).to receive(:perform_in)
+
+ expect(File.exist?(path_in_dir)).to be(false)
- namespace.destroy
+ namespace.destroy
+
+ expect(File.exist?(deleted_path_in_dir)).to be(false)
+ end
+
+ it 'removes the exports folder' do
+ expect(namespace).to receive(:remove_exports!)
+
+ namespace.destroy
+ end
end
end
@@ -567,8 +611,8 @@ describe Namespace do
end
describe '#remove_exports' do
- let(:legacy_project) { create(:project, :with_export, namespace: namespace) }
- let(:hashed_project) { create(:project, :with_export, :hashed, namespace: namespace) }
+ let(:legacy_project) { create(:project, :with_export, :legacy_storage, namespace: namespace) }
+ let(:hashed_project) { create(:project, :with_export, namespace: namespace) }
let(:export_path) { Dir.mktmpdir('namespace_remove_exports_spec') }
let(:legacy_export) { legacy_project.export_project_path }
let(:hashed_export) { hashed_project.export_project_path }
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
new file mode 100644
index 00000000000..eda0e1da835
--- /dev/null
+++ b/spec/models/notification_recipient_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe NotificationRecipient do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, namespace: user.namespace) }
+ let(:target) { create(:issue, project: project) }
+
+ subject(:recipient) { described_class.new(user, :watch, target: target, project: project) }
+
+ it 'denies access to a target when cross project access is denied' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ expect(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(false)
+
+ expect(recipient.has_access?).to be_falsy
+ end
+end
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index 9d12f96c642..95713d8b85b 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -1,6 +1,10 @@
require 'spec_helper'
describe PagesDomain do
+ using RSpec::Parameterized::TableSyntax
+
+ subject(:pages_domain) { described_class.new }
+
describe 'associations' do
it { is_expected.to belong_to(:project) }
end
@@ -64,19 +68,51 @@ describe PagesDomain do
end
end
+ describe 'validations' do
+ it { is_expected.to validate_presence_of(:verification_code) }
+ end
+
+ describe '#verification_code' do
+ subject { pages_domain.verification_code }
+
+ it 'is set automatically with 128 bits of SecureRandom data' do
+ expect(SecureRandom).to receive(:hex).with(16) { 'verification code' }
+
+ is_expected.to eq('verification code')
+ end
+ end
+
+ describe '#keyed_verification_code' do
+ subject { pages_domain.keyed_verification_code }
+
+ it { is_expected.to eq("gitlab-pages-verification-code=#{pages_domain.verification_code}") }
+ end
+
+ describe '#verification_domain' do
+ subject { pages_domain.verification_domain }
+
+ it { is_expected.to be_nil }
+
+ it 'is a well-known subdomain if the domain is present' do
+ pages_domain.domain = 'example.com'
+
+ is_expected.to eq('_gitlab-pages-verification-code.example.com')
+ end
+ end
+
describe '#url' do
subject { domain.url }
context 'without the certificate' do
let(:domain) { build(:pages_domain, certificate: '') }
- it { is_expected.to eq('http://my.domain.com') }
+ it { is_expected.to eq("http://#{domain.domain}") }
end
context 'with a certificate' do
let(:domain) { build(:pages_domain, :with_certificate) }
- it { is_expected.to eq('https://my.domain.com') }
+ it { is_expected.to eq("https://#{domain.domain}") }
end
end
@@ -154,4 +190,108 @@ describe PagesDomain do
# We test only existence of output, since the output is long
it { is_expected.not_to be_empty }
end
+
+ describe '#update_daemon' do
+ it 'runs when the domain is created' do
+ domain = build(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.save!
+ end
+
+ it 'runs when the domain is destroyed' do
+ domain = create(:pages_domain)
+
+ expect(domain).to receive(:update_daemon)
+
+ domain.destroy!
+ end
+
+ it 'delegates to Projects::UpdatePagesConfigurationService' do
+ service = instance_double('Projects::UpdatePagesConfigurationService')
+ expect(Projects::UpdatePagesConfigurationService).to receive(:new) { service }
+ expect(service).to receive(:execute)
+
+ create(:pages_domain)
+ end
+
+ context 'configuration updates when attributes change' do
+ set(:project1) { create(:project) }
+ set(:project2) { create(:project) }
+ set(:domain) { create(:pages_domain) }
+
+ where(:attribute, :old_value, :new_value, :update_expected) do
+ now = Time.now
+ future = now + 1.day
+
+ :project | nil | :project1 | true
+ :project | :project1 | :project1 | false
+ :project | :project1 | :project2 | true
+ :project | :project1 | nil | true
+
+ # domain can't be set to nil
+ :domain | 'a.com' | 'a.com' | false
+ :domain | 'a.com' | 'b.com' | true
+
+ # verification_code can't be set to nil
+ :verification_code | 'foo' | 'foo' | false
+ :verification_code | 'foo' | 'bar' | false
+
+ :verified_at | nil | now | false
+ :verified_at | now | now | false
+ :verified_at | now | future | false
+ :verified_at | now | nil | false
+
+ :enabled_until | nil | now | true
+ :enabled_until | now | now | false
+ :enabled_until | now | future | false
+ :enabled_until | now | nil | true
+ end
+
+ with_them do
+ it 'runs if a relevant attribute has changed' do
+ a = old_value.is_a?(Symbol) ? send(old_value) : old_value
+ b = new_value.is_a?(Symbol) ? send(new_value) : new_value
+
+ domain.update!(attribute => a)
+
+ if update_expected
+ expect(domain).to receive(:update_daemon)
+ else
+ expect(domain).not_to receive(:update_daemon)
+ end
+
+ domain.update!(attribute => b)
+ end
+ end
+
+ context 'TLS configuration' do
+ set(:domain_with_tls) { create(:pages_domain, :with_key, :with_certificate) }
+
+ let(:cert1) { domain_with_tls.certificate }
+ let(:cert2) { cert1 + ' ' }
+ let(:key1) { domain_with_tls.key }
+ let(:key2) { key1 + ' ' }
+
+ it 'updates when added' do
+ expect(domain).to receive(:update_daemon)
+
+ domain.update!(key: key1, certificate: cert1)
+ end
+
+ it 'updates when changed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: key2, certificate: cert2)
+ end
+
+ it 'updates when removed' do
+ expect(domain_with_tls).to receive(:update_daemon)
+
+ domain_with_tls.update!(key: nil, certificate: nil)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index ed17e019d42..6693e5783a5 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -223,8 +223,8 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with cluster for all environments without prometheus installed' do
context 'without environment supplied' do
- it 'raises PrometheusError because cluster was not found' do
- expect { service.client }.to raise_error(Gitlab::PrometheusError, /couldn't find cluster with Prometheus installed/)
+ it 'raises PrometheusClient::Error because cluster was not found' do
+ expect { service.client }.to raise_error(Gitlab::PrometheusClient::Error, /couldn't find cluster with Prometheus installed/)
end
end
@@ -242,8 +242,8 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
context 'with prod environment supplied' do
let!(:environment) { create(:environment, project: project, name: 'prod') }
- it 'raises PrometheusError because cluster was not found' do
- expect { service.client }.to raise_error(Gitlab::PrometheusError, /couldn't find cluster with Prometheus installed/)
+ it 'raises PrometheusClient::Error because cluster was not found' do
+ expect { service.client }.to raise_error(Gitlab::PrometheusClient::Error, /couldn't find cluster with Prometheus installed/)
end
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index c6ca038a2ba..56c2d7b953e 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1473,6 +1473,13 @@ describe Project do
expect(project.user_can_push_to_empty_repo?(user)).to be_truthy
end
+
+ it 'returns false when the repo is not empty' do
+ project.add_master(user)
+ expect(project).to receive(:empty_repo?).and_return(false)
+
+ expect(project.user_can_push_to_empty_repo?(user)).to be_falsey
+ end
end
describe '#container_registry_url' do
@@ -2092,7 +2099,7 @@ describe Project do
context 'when the ref is a protected branch' do
before do
- create(:protected_branch, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -2100,7 +2107,7 @@ describe Project do
context 'when the ref is a protected tag' do
before do
- create(:protected_tag, name: 'ref', project: project)
+ allow(project).to receive(:protected_for?).with('ref').and_return(true)
end
it_behaves_like 'ref is protected'
@@ -2125,6 +2132,8 @@ describe Project do
context 'when the ref is a protected branch' do
before do
+ allow(project).to receive(:repository).and_call_original
+ allow(project).to receive_message_chain(:repository, :branch_exists?).and_return(true)
create(:protected_branch, name: 'ref', project: project)
end
@@ -2135,6 +2144,8 @@ describe Project do
context 'when the ref is a protected tag' do
before do
+ allow(project).to receive_message_chain(:repository, :branch_exists?).and_return(false)
+ allow(project).to receive_message_chain(:repository, :tag_exists?).and_return(true)
create(:protected_tag, name: 'ref', project: project)
end
@@ -2504,6 +2515,7 @@ describe Project do
end
describe '#remove_exports' do
+ let(:legacy_project) { create(:project, :legacy_storage, :with_export) }
let(:project) { create(:project, :with_export) }
it 'removes the exports directory for the project' do
@@ -2516,15 +2528,29 @@ describe Project do
expect(File.exist?(project.export_path)).to be_falsy
end
- it 'is a no-op when there is no namespace' do
+ it 'is a no-op on legacy projects when there is no namespace' do
+ export_path = legacy_project.export_path
+
+ legacy_project.update_column(:namespace_id, nil)
+
+ expect(FileUtils).not_to receive(:rm_rf).with(export_path)
+
+ legacy_project.remove_exports
+
+ expect(File.exist?(export_path)).to be_truthy
+ end
+
+ it 'runs on hashed storage projects when there is no namespace' do
export_path = project.export_path
+
project.update_column(:namespace_id, nil)
- expect(FileUtils).not_to receive(:rm_rf).with(export_path)
+ allow(FileUtils).to receive(:rm_rf).and_call_original
+ expect(FileUtils).to receive(:rm_rf).with(export_path).and_call_original
project.remove_exports
- expect(File.exist?(export_path)).to be_truthy
+ expect(File.exist?(export_path)).to be_falsy
end
it 'is run when the project is destroyed' do
@@ -2545,7 +2571,7 @@ describe Project do
end
context 'legacy storage' do
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :legacy_storage) }
let(:gitlab_shell) { Gitlab::Shell.new }
let(:project_storage) { project.send(:storage) }
@@ -2719,6 +2745,8 @@ describe Project do
let(:project) { create(:project, :repository, skip_disk_validation: true) }
let(:gitlab_shell) { Gitlab::Shell.new }
let(:hash) { Digest::SHA2.hexdigest(project.id.to_s) }
+ let(:hashed_prefix) { File.join('@hashed', hash[0..1], hash[2..3]) }
+ let(:hashed_path) { File.join(hashed_prefix, hash) }
before do
stub_application_setting(hashed_storage_enabled: true)
@@ -2744,14 +2772,12 @@ describe Project do
describe '#base_dir' do
it 'returns base_dir based on hash of project id' do
- expect(project.base_dir).to eq("@hashed/#{hash[0..1]}/#{hash[2..3]}")
+ expect(project.base_dir).to eq(hashed_prefix)
end
end
describe '#disk_path' do
it 'returns disk_path based on hash of project id' do
- hashed_path = "@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}"
-
expect(project.disk_path).to eq(hashed_path)
end
end
@@ -2760,7 +2786,7 @@ describe Project do
it 'delegates to gitlab_shell to ensure namespace is created' do
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
- expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, "@hashed/#{hash[0..1]}/#{hash[2..3]}")
+ expect(gitlab_shell).to receive(:add_namespace).with(project.repository_storage_path, hashed_prefix)
project.ensure_storage_path_exists
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index a6d48e369ac..0bc07dc7a85 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -873,6 +873,18 @@ describe Repository do
expect(repository.license_key).to be_nil
end
+ it 'returns nil when the commit SHA does not exist' do
+ allow(repository.head_commit).to receive(:sha).and_return('1' * 40)
+
+ expect(repository.license_key).to be_nil
+ end
+
+ it 'returns nil when master does not exist' do
+ repository.rm_branch(user, 'master')
+
+ expect(repository.license_key).to be_nil
+ end
+
it 'returns the license key' do
repository.create_file(user, 'LICENSE',
Licensee::License.new('mit').content,
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index cb02d526a98..3531de244bd 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -496,6 +496,14 @@ describe User do
user2.update_tracked_fields!(request)
end.to change { user2.reload.current_sign_in_at }
end
+
+ it 'does not write if the DB is in read-only mode' do
+ expect(Gitlab::Database).to receive(:read_only?).and_return(true)
+
+ expect do
+ user.update_tracked_fields!(request)
+ end.not_to change { user.reload.current_sign_in_at }
+ end
end
shared_context 'user keys' do
@@ -893,6 +901,14 @@ describe User do
end
end
+ describe '.find_for_database_authentication' do
+ it 'strips whitespace from login' do
+ user = create(:user)
+
+ expect(described_class.find_for_database_authentication({ login: " #{user.username} " })).to eq user
+ end
+ end
+
describe '.find_by_any_email' do
it 'finds by primary email' do
user = create(:user, email: 'foo@example.com')
@@ -1586,14 +1602,37 @@ describe User do
describe '#authorized_groups' do
let!(:user) { create(:user) }
let!(:private_group) { create(:group) }
+ let!(:child_group) { create(:group, parent: private_group) }
+
+ let!(:project_group) { create(:group) }
+ let!(:project) { create(:project, group: project_group) }
before do
private_group.add_user(user, Gitlab::Access::MASTER)
+ project.add_master(user)
end
subject { user.authorized_groups }
- it { is_expected.to eq([private_group]) }
+ it { is_expected.to contain_exactly private_group, project_group }
+ end
+
+ describe '#membership_groups' do
+ let!(:user) { create(:user) }
+ let!(:parent_group) { create(:group) }
+ let!(:child_group) { create(:group, parent: parent_group) }
+
+ before do
+ parent_group.add_user(user, Gitlab::Access::MASTER)
+ end
+
+ subject { user.membership_groups }
+
+ if Group.supports_nested_groups?
+ it { is_expected.to contain_exactly parent_group, child_group }
+ else
+ it { is_expected.to contain_exactly parent_group }
+ end
end
describe '#authorized_projects', :delete do
diff --git a/spec/policies/issuable_policy_spec.rb b/spec/policies/issuable_policy_spec.rb
index 2cf669e8191..d1bf98995e7 100644
--- a/spec/policies/issuable_policy_spec.rb
+++ b/spec/policies/issuable_policy_spec.rb
@@ -1,12 +1,14 @@
require 'spec_helper'
describe IssuablePolicy, models: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:issue) { create(:issue, project: project) }
+ let(:policies) { described_class.new(user, issue) }
+
describe '#rules' do
context 'when discussion is locked for the issuable' do
- let(:user) { create(:user) }
- let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project, discussion_locked: true) }
- let(:policies) { described_class.new(user, issue) }
context 'when the user is not a project member' do
it 'can not create a note' do
diff --git a/spec/policies/issue_policy_spec.rb b/spec/policies/issue_policy_spec.rb
index a4af9361ea6..793b724bfca 100644
--- a/spec/policies/issue_policy_spec.rb
+++ b/spec/policies/issue_policy_spec.rb
@@ -30,41 +30,41 @@ describe IssuePolicy do
end
it 'does not allow non-members to read issues' do
- expect(permissions(non_member, issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(non_member, issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(non_member, issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(non_member, issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows guests to read issues' do
- expect(permissions(guest, issue)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue)
- expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin issues' do
- expect(permissions(reporter, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters from group links to read, update, and admin issues' do
- expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their issues' do
- expect(permissions(author, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their issues' do
- expect(permissions(assignee, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
@@ -73,37 +73,37 @@ describe IssuePolicy do
let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) }
it 'does not allow non-members to read confidential issues' do
- expect(permissions(non_member, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(non_member, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(non_member, confidential_issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(non_member, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'does not allow guests to read confidential issues' do
- expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin confidential issues' do
- expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters from group links to read, update, and admin confidential issues' do
- expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their confidential issues' do
- expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their confidential issues' do
- expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
end
end
@@ -123,36 +123,36 @@ describe IssuePolicy do
end
it 'allows guests to read issues' do
- expect(permissions(guest, issue)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue)).to be_disallowed(:update_issue, :admin_issue)
- expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(guest, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(guest, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin issues' do
- expect(permissions(reporter, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters from group links to read, update, and admin issues' do
- expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their issues' do
- expect(permissions(author, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(author, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(author, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their issues' do
- expect(permissions(assignee, issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue)
+ expect(permissions(assignee, issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid)
expect(permissions(assignee, issue_no_assignee)).to be_disallowed(:update_issue, :admin_issue)
end
@@ -161,32 +161,32 @@ describe IssuePolicy do
let(:confidential_issue_no_assignee) { create(:issue, :confidential, project: project) }
it 'does not allow guests to read confidential issues' do
- expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(guest, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporters to read, update, and admin confidential issues' do
- expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows reporter from group links to read, update, and admin confidential issues' do
- expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :update_issue, :admin_issue)
- expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
+ expect(permissions(reporter_from_group_link, confidential_issue_no_assignee)).to be_allowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue authors to read and update their confidential issues' do
- expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(author, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(author, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(author, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
it 'allows issue assignees to read and update their confidential issues' do
- expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :update_issue)
+ expect(permissions(assignee, confidential_issue)).to be_allowed(:read_issue, :read_issue_iid, :update_issue)
expect(permissions(assignee, confidential_issue)).to be_disallowed(:admin_issue)
- expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :update_issue, :admin_issue)
+ expect(permissions(assignee, confidential_issue_no_assignee)).to be_disallowed(:read_issue, :read_issue_iid, :update_issue, :admin_issue)
end
end
end
diff --git a/spec/policies/personal_snippet_policy_spec.rb b/spec/policies/personal_snippet_policy_spec.rb
index b70c8646a3d..50bb0899eba 100644
--- a/spec/policies/personal_snippet_policy_spec.rb
+++ b/spec/policies/personal_snippet_policy_spec.rb
@@ -1,5 +1,6 @@
require 'spec_helper'
+# Snippet visibility scenarios are included in more details in spec/support/snippet_visibility.rb
describe PersonalSnippetPolicy do
let(:regular_user) { create(:user) }
let(:external_user) { create(:user, :external) }
diff --git a/spec/policies/project_snippet_policy_spec.rb b/spec/policies/project_snippet_policy_spec.rb
index cdba1b09fc1..4d32e06b553 100644
--- a/spec/policies/project_snippet_policy_spec.rb
+++ b/spec/policies/project_snippet_policy_spec.rb
@@ -1,5 +1,6 @@
require 'spec_helper'
+# Snippet visibility scenarios are included in more details in spec/support/snippet_visibility.rb
describe ProjectSnippetPolicy do
let(:regular_user) { create(:user) }
let(:external_user) { create(:user, :external) }
diff --git a/spec/presenters/project_presenter_spec.rb b/spec/presenters/project_presenter_spec.rb
new file mode 100644
index 00000000000..f8c93d91ec5
--- /dev/null
+++ b/spec/presenters/project_presenter_spec.rb
@@ -0,0 +1,397 @@
+require 'spec_helper'
+
+describe ProjectPresenter do
+ let(:user) { create(:user) }
+
+ describe '#license_short_name' do
+ let(:project) { create(:project) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ context 'when project.repository has a license_key' do
+ it 'returns the nickname of the license if present' do
+ allow(project.repository).to receive(:license_key).and_return('agpl-3.0')
+
+ expect(presenter.license_short_name).to eq('GNU AGPLv3')
+ end
+
+ it 'returns the name of the license if nickname is not present' do
+ allow(project.repository).to receive(:license_key).and_return('mit')
+
+ expect(presenter.license_short_name).to eq('MIT License')
+ end
+ end
+
+ context 'when project.repository has no license_key but a license_blob' do
+ it 'returns LICENSE' do
+ allow(project.repository).to receive(:license_key).and_return(nil)
+
+ expect(presenter.license_short_name).to eq('LICENSE')
+ end
+ end
+ end
+
+ describe '#default_view' do
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ context 'user not signed in' do
+ let(:user) { nil }
+
+ context 'when repository is empty' do
+ let(:project) { create(:project_empty_repo, :public) }
+
+ it 'returns activity if user has repository access' do
+ allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true)
+
+ expect(presenter.default_view).to eq('activity')
+ end
+
+ it 'returns activity if user does not have repository access' do
+ allow(project).to receive(:can?).with(nil, :download_code, project).and_return(false)
+
+ expect(presenter.default_view).to eq('activity')
+ end
+ end
+
+ context 'when repository is not empty' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it 'returns files and readme if user has repository access' do
+ allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(true)
+
+ expect(presenter.default_view).to eq('files')
+ end
+
+ it 'returns activity if user does not have repository access' do
+ allow(presenter).to receive(:can?).with(nil, :download_code, project).and_return(false)
+
+ expect(presenter.default_view).to eq('activity')
+ end
+ end
+ end
+
+ context 'user signed in' do
+ let(:user) { create(:user, :readme) }
+ let(:project) { create(:project, :public, :repository) }
+
+ context 'when the user is allowed to see the code' do
+ it 'returns the project view' do
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(true)
+
+ expect(presenter.default_view).to eq('readme')
+ end
+ end
+
+ context 'with wikis enabled and the right policy for the user' do
+ before do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ end
+
+ it 'returns wiki if the user has the right policy' do
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(true)
+
+ expect(presenter.default_view).to eq('wiki')
+ end
+
+ it 'returns customize_workflow if the user does not have the right policy' do
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(presenter.default_view).to eq('customize_workflow')
+ end
+ end
+
+ context 'with issues as a feature available' do
+ it 'return issues' do
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(presenter.default_view).to eq('projects/issues/issues')
+ end
+ end
+
+ context 'with no activity, no wikies and no issues' do
+ it 'returns customize_workflow as default' do
+ project.project_feature.update_attribute(:issues_access_level, 0)
+ allow(presenter).to receive(:can?).with(user, :download_code, project).and_return(false)
+ allow(presenter).to receive(:can?).with(user, :read_wiki, project).and_return(false)
+
+ expect(presenter.default_view).to eq('customize_workflow')
+ end
+ end
+ end
+ end
+
+ describe '#can_current_user_push_code?' do
+ let(:project) { create(:project, :repository) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ context 'empty repo' do
+ let(:project) { create(:project) }
+
+ it 'returns true if user can push_code' do
+ project.add_developer(user)
+
+ expect(presenter.can_current_user_push_code?).to be(true)
+ end
+
+ it 'returns false if user cannot push_code' do
+ project.add_reporter(user)
+
+ expect(presenter.can_current_user_push_code?).to be(false)
+ end
+ end
+
+ context 'not empty repo' do
+ let(:project) { create(:project, :repository) }
+
+ it 'returns true if user can push to default branch' do
+ project.add_developer(user)
+
+ expect(presenter.can_current_user_push_code?).to be(true)
+ end
+
+ it 'returns false if default branch is protected' do
+ project.add_developer(user)
+ create(:protected_branch, project: project, name: project.default_branch)
+
+ expect(presenter.can_current_user_push_code?).to be(false)
+ end
+ end
+ end
+
+ context 'statistics anchors' do
+ let(:project) { create(:project, :repository) }
+ let(:presenter) { described_class.new(project, current_user: user) }
+
+ describe '#files_anchor_data' do
+ it 'returns files data' do
+ expect(presenter.files_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Files (0 Bytes)',
+ link: presenter.project_tree_path(project)))
+ end
+ end
+
+ describe '#commits_anchor_data' do
+ it 'returns commits data' do
+ expect(presenter.commits_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Commits (0)',
+ link: presenter.project_commits_path(project, project.repository.root_ref)))
+ end
+ end
+
+ describe '#branches_anchor_data' do
+ it 'returns branches data' do
+ expect(presenter.branches_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: "Branches (#{project.repository.branches.size})",
+ link: presenter.project_branches_path(project)))
+ end
+ end
+
+ describe '#tags_anchor_data' do
+ it 'returns tags data' do
+ expect(presenter.tags_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: "Tags (#{project.repository.tags.size})",
+ link: presenter.project_tags_path(project)))
+ end
+ end
+
+ describe '#new_file_anchor_data' do
+ it 'returns new file data if user can push' do
+ project.add_developer(user)
+
+ expect(presenter.new_file_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: "New file",
+ link: presenter.project_new_blob_path(project, 'master'),
+ class_modifier: 'new'))
+ end
+
+ it 'returns nil if user cannot push' do
+ expect(presenter.new_file_anchor_data).to be_nil
+ end
+ end
+
+ describe '#readme_anchor_data' do
+ context 'when user can push and README does not exists' do
+ it 'returns anchor data' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:readme).and_return(nil)
+
+ expect(presenter.readme_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add Readme',
+ link: presenter.add_readme_path))
+ end
+ end
+
+ context 'when README exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:readme).and_return(double(name: 'readme'))
+
+ expect(presenter.readme_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Readme',
+ link: presenter.readme_path))
+ end
+ end
+ end
+
+ describe '#changelog_anchor_data' do
+ context 'when user can push and CHANGELOG does not exists' do
+ it 'returns anchor data' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:changelog).and_return(nil)
+
+ expect(presenter.changelog_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add Changelog',
+ link: presenter.add_changelog_path))
+ end
+ end
+
+ context 'when CHANGELOG exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:changelog).and_return(double(name: 'foo'))
+
+ expect(presenter.changelog_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Changelog',
+ link: presenter.changelog_path))
+ end
+ end
+ end
+
+ describe '#license_anchor_data' do
+ context 'when user can push and LICENSE does not exists' do
+ it 'returns anchor data' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:license_blob).and_return(nil)
+
+ expect(presenter.license_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add License',
+ link: presenter.add_license_path))
+ end
+ end
+
+ context 'when LICENSE exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:license_blob).and_return(double(name: 'foo'))
+
+ expect(presenter.license_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: presenter.license_short_name,
+ link: presenter.license_path))
+ end
+ end
+ end
+
+ describe '#contribution_guide_anchor_data' do
+ context 'when user can push and CONTRIBUTING does not exists' do
+ it 'returns anchor data' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:contribution_guide).and_return(nil)
+
+ expect(presenter.contribution_guide_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add Contribution guide',
+ link: presenter.add_contribution_guide_path))
+ end
+ end
+
+ context 'when CONTRIBUTING exists' do
+ it 'returns anchor data' do
+ allow(project.repository).to receive(:contribution_guide).and_return(double(name: 'foo'))
+
+ expect(presenter.contribution_guide_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Contribution guide',
+ link: presenter.contribution_guide_path))
+ end
+ end
+ end
+
+ describe '#autodevops_anchor_data' do
+ context 'when Auto Devops is enabled' do
+ it 'returns anchor data' do
+ allow(project).to receive(:auto_devops_enabled?).and_return(true)
+
+ expect(presenter.autodevops_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Auto DevOps enabled',
+ link: nil))
+ end
+ end
+
+ context 'when user can admin pipeline and CI yml does not exists' do
+ it 'returns anchor data' do
+ project.add_master(user)
+ allow(project).to receive(:auto_devops_enabled?).and_return(false)
+ allow(project.repository).to receive(:gitlab_ci_yml).and_return(nil)
+
+ expect(presenter.autodevops_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Enable Auto DevOps',
+ link: presenter.project_settings_ci_cd_path(project, anchor: 'js-general-pipeline-settings')))
+ end
+ end
+ end
+
+ describe '#kubernetes_cluster_anchor_data' do
+ context 'when user can create Kubernetes cluster' do
+ it 'returns link to cluster if only one exists' do
+ project.add_master(user)
+ cluster = create(:cluster, projects: [project])
+
+ expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Kubernetes configured',
+ link: presenter.project_cluster_path(project, cluster)))
+ end
+
+ it 'returns link to clusters page if more than one exists' do
+ project.add_master(user)
+ create(:cluster, projects: [project])
+ create(:cluster, projects: [project])
+
+ expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: true,
+ label: 'Kubernetes configured',
+ link: presenter.project_clusters_path(project)))
+ end
+
+ it 'returns link to create a cluster if no cluster exists' do
+ project.add_master(user)
+
+ expect(presenter.kubernetes_cluster_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Add Kubernetes cluster',
+ link: presenter.new_project_cluster_path(project)))
+ end
+ end
+
+ context 'when user cannot create Kubernetes cluster' do
+ it 'returns nil' do
+ expect(presenter.kubernetes_cluster_anchor_data).to be_nil
+ end
+ end
+ end
+
+ describe '#koding_anchor_data' do
+ it 'returns link to setup Koding if user can push and no koding YML exists' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:koding_yml).and_return(nil)
+ allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
+
+ expect(presenter.koding_anchor_data).to eq(OpenStruct.new(enabled: false,
+ label: 'Set up Koding',
+ link: presenter.add_koding_stack_path))
+ end
+
+ it 'returns nil if user cannot push' do
+ expect(presenter.koding_anchor_data).to be_nil
+ end
+
+ it 'returns nil if koding is not enabled' do
+ project.add_developer(user)
+ allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(false)
+
+ expect(presenter.koding_anchor_data).to be_nil
+ end
+
+ it 'returns nil if koding YML already exists' do
+ project.add_developer(user)
+ allow(project.repository).to receive(:koding_yml).and_return(double)
+ allow(Gitlab::CurrentSettings).to receive(:koding_enabled?).and_return(true)
+
+ expect(presenter.koding_anchor_data).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index ff5f207487b..ad3eec88952 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -465,6 +465,72 @@ describe API::Commits do
end
end
+ describe 'GET /projects/:id/repository/commits/:sha/refs' do
+ let(:project) { create(:project, :public, :repository) }
+ let(:tag) { project.repository.find_tag('v1.1.0') }
+ let(:commit_id) { tag.dereferenced_target.id }
+ let(:route) { "/projects/#{project_id}/repository/commits/#{commit_id}/refs" }
+
+ context 'when ref does not exist' do
+ let(:commit_id) { 'unknown' }
+
+ it_behaves_like '404 response' do
+ let(:request) { get api(route, current_user) }
+ let(:message) { '404 Commit Not Found' }
+ end
+ end
+
+ context 'when repository is disabled' do
+ include_context 'disabled repository'
+
+ it_behaves_like '403 response' do
+ let(:request) { get api(route, current_user) }
+ end
+ end
+
+ context 'for a valid commit' do
+ it 'returns all refs with no scope' do
+ get api(route, current_user), per_page: 100
+
+ refs = project.repository.branch_names_contains(commit_id).map {|name| ['branch', name]}
+ refs.concat(project.repository.tag_names_contains(commit_id).map {|name| ['tag', name]})
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
+ expect(json_response).to be_an Array
+ expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
+ end
+
+ it 'returns all refs' do
+ get api(route, current_user), type: 'all', per_page: 100
+
+ refs = project.repository.branch_names_contains(commit_id).map {|name| ['branch', name]}
+ refs.concat(project.repository.tag_names_contains(commit_id).map {|name| ['tag', name]})
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
+ end
+
+ it 'returns the branch refs' do
+ get api(route, current_user), type: 'branch', per_page: 100
+
+ refs = project.repository.branch_names_contains(commit_id).map {|name| ['branch', name]}
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
+ end
+
+ it 'returns the tag refs' do
+ get api(route, current_user), type: 'tag', per_page: 100
+
+ refs = project.repository.tag_names_contains(commit_id).map {|name| ['tag', name]}
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.map { |r| [r['type'], r['name']] }.compact).to eq(refs)
+ end
+ end
+ end
+
describe 'GET /projects/:id/repository/commits/:sha' do
let(:commit) { project.repository.commit }
let(:commit_id) { commit.id }
@@ -632,6 +698,7 @@ describe API::Commits do
get api(route, current_user)
expect(response).to have_gitlab_http_status(200)
+ expect(response).to include_pagination_headers
expect(json_response.size).to be >= 1
expect(json_response.first.keys).to include 'diff'
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index ea6b0a71849..827f4c04324 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::Internal do
let(:user) { create(:user) }
let(:key) { create(:key, user: user) }
- let(:project) { create(:project, :repository) }
+ let(:project) { create(:project, :repository, :wiki_repo) }
let(:secret_token) { Gitlab::Shell.secret_token }
let(:gl_repository) { "project-#{project.id}" }
let(:reference_counter) { double('ReferenceCounter') }
@@ -366,20 +366,9 @@ describe API::Internal do
end
end
- context 'project as /namespace/project' do
- it do
- push(key, project_with_repo_path('/' + project.full_path))
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq(project.repository.path_to_repo)
- expect(json_response["gl_repository"]).to eq("project-#{project.id}")
- end
- end
-
context 'project as namespace/project' do
it do
- push(key, project_with_repo_path(project.full_path))
+ push(key, project)
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
@@ -496,8 +485,10 @@ describe API::Internal do
end
context 'project does not exist' do
- it do
- pull(key, project_with_repo_path('gitlab/notexist'))
+ it 'returns a 200 response with status: false' do
+ project.destroy
+
+ pull(key, project)
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_falsey
@@ -569,6 +560,7 @@ describe API::Internal do
end
context 'the project path was changed' do
+ let(:project) { create(:project, :repository, :legacy_storage) }
let!(:old_path_to_repo) { project.repository.path_to_repo }
let!(:repository) { project.repository }
@@ -858,9 +850,14 @@ describe API::Internal do
end
end
- def project_with_repo_path(path)
- double().tap do |fake_project|
- allow(fake_project).to receive_message_chain('repository.path_to_repo' => path)
+ def gl_repository_for(project_or_wiki)
+ case project_or_wiki
+ when ProjectWiki
+ project_or_wiki.project.gl_repository(is_wiki: true)
+ when Project
+ project_or_wiki.gl_repository(is_wiki: false)
+ else
+ nil
end
end
@@ -868,18 +865,8 @@ describe API::Internal do
post(
api("/internal/allowed"),
key_id: key.id,
- project: project.repository.path_to_repo,
- action: 'git-upload-pack',
- secret_token: secret_token,
- protocol: protocol
- )
- end
-
- def pull_with_path(key, path_to_repo, protocol = 'ssh')
- post(
- api("/internal/allowed"),
- key_id: key.id,
- project: path_to_repo,
+ project: project.full_path,
+ gl_repository: gl_repository_for(project),
action: 'git-upload-pack',
secret_token: secret_token,
protocol: protocol
@@ -891,20 +878,8 @@ describe API::Internal do
api("/internal/allowed"),
changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
key_id: key.id,
- project: project.repository.path_to_repo,
- action: 'git-receive-pack',
- secret_token: secret_token,
- protocol: protocol,
- env: env
- )
- end
-
- def push_with_path(key, path_to_repo, protocol = 'ssh', env: nil)
- post(
- api("/internal/allowed"),
- changes: 'd14d6c0abdd253381df51a723d58691b2ee1ab08 570e7b2abdd848b95f2f578043fc23bd6f6fd24d refs/heads/master',
- key_id: key.id,
- project: path_to_repo,
+ project: project.full_path,
+ gl_repository: gl_repository_for(project),
action: 'git-receive-pack',
secret_token: secret_token,
protocol: protocol,
@@ -917,7 +892,8 @@ describe API::Internal do
api("/internal/allowed"),
ref: 'master',
key_id: key.id,
- project: project.repository.path_to_repo,
+ project: project.full_path,
+ gl_repository: gl_repository_for(project),
action: 'git-upload-archive',
secret_token: secret_token,
protocol: 'ssh'
@@ -929,7 +905,7 @@ describe API::Internal do
api("/internal/lfs_authenticate"),
key_id: key_id,
secret_token: secret_token,
- project: project.repository.path_to_repo
+ project: project.full_path
)
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 13db40d21a5..d1569e5d650 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::Issues, :mailer do
+describe API::Issues do
set(:user) { create(:user) }
set(:project) do
create(:project, :public, creator_id: user.id, namespace: user.namespace)
@@ -932,18 +932,6 @@ describe API::Issues, :mailer do
expect(json_response['error']).to eq('confidential is invalid')
end
- it "sends notifications for subscribers of newly added labels" do
- label = project.labels.first
- label.toggle_subscription(user2, project)
-
- perform_enqueued_jobs do
- post api("/projects/#{project.id}/issues", user),
- title: 'new issue', labels: label.title
- end
-
- should_email(user2)
- end
-
it "returns a 400 bad request if title not given" do
post api("/projects/#{project.id}/issues", user), labels: 'label, label2'
expect(response).to have_gitlab_http_status(400)
@@ -1246,18 +1234,6 @@ describe API::Issues, :mailer do
expect(json_response['labels']).to eq([label.title])
end
- it "sends notifications for subscribers of newly added labels when issue is updated" do
- label = create(:label, title: 'foo', color: '#FFAABB', project: project)
- label.toggle_subscription(user2, project)
-
- perform_enqueued_jobs do
- put api("/projects/#{project.id}/issues/#{issue.iid}", user),
- title: 'updated title', labels: label.title
- end
-
- should_email(user2)
- end
-
it 'removes all labels' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user), labels: ''
@@ -1380,7 +1356,7 @@ describe API::Issues, :mailer do
end
describe '/projects/:id/issues/:issue_iid/move' do
- let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
+ let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
@@ -1441,7 +1417,7 @@ describe API::Issues, :mailer do
context 'when source project does not exist' do
it 'returns 404 when trying to move an issue' do
- post api("/projects/12345/issues/#{issue.iid}/move", user),
+ post api("/projects/0/issues/#{issue.iid}/move", user),
to_project_id: target_project.id
expect(response).to have_gitlab_http_status(404)
@@ -1452,7 +1428,7 @@ describe API::Issues, :mailer do
context 'when target project does not exist' do
it 'returns 404 when trying to move an issue' do
post api("/projects/#{project.id}/issues/#{issue.iid}/move", user),
- to_project_id: 12345
+ to_project_id: 0
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/pages_domains_spec.rb b/spec/requests/api/pages_domains_spec.rb
index 5d01dc37f0e..025165622b7 100644
--- a/spec/requests/api/pages_domains_spec.rb
+++ b/spec/requests/api/pages_domains_spec.rb
@@ -1,7 +1,7 @@
require 'rails_helper'
describe API::PagesDomains do
- set(:project) { create(:project) }
+ set(:project) { create(:project, path: 'my.project') }
set(:user) { create(:user) }
set(:admin) { create(:admin) }
@@ -16,6 +16,7 @@ describe API::PagesDomains do
let(:route) { "/projects/#{project.id}/pages/domains" }
let(:route_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain.domain}" }
+ let(:route_domain_path) { "/projects/#{project.path_with_namespace.gsub('/', '%2F')}/pages/domains/#{pages_domain.domain}" }
let(:route_secure_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_secure.domain}" }
let(:route_expired_domain) { "/projects/#{project.id}/pages/domains/#{pages_domain_expired.domain}" }
let(:route_vacant_domain) { "/projects/#{project.id}/pages/domains/www.vacant-domain.test" }
@@ -144,6 +145,16 @@ describe API::PagesDomains do
expect(json_response['certificate']).to be_nil
end
+ it 'returns pages domain with project path' do
+ get api(route_domain_path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/pages_domain/detail')
+ expect(json_response['domain']).to eq(pages_domain.domain)
+ expect(json_response['url']).to eq(pages_domain.url)
+ expect(json_response['certificate']).to be_nil
+ end
+
it 'returns pages domain with a certificate' do
get api(route_secure_domain, user)
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
new file mode 100644
index 00000000000..987f6e26971
--- /dev/null
+++ b/spec/requests/api/project_import_spec.rb
@@ -0,0 +1,102 @@
+require 'spec_helper'
+
+describe API::ProjectImport do
+ let(:export_path) { "#{Dir.tmpdir}/project_export_spec" }
+ let(:user) { create(:user) }
+ let(:file) { File.join(Rails.root, 'spec', 'features', 'projects', 'import_export', 'test_project_export.tar.gz') }
+ let(:namespace) { create(:group) }
+ before do
+ allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
+
+ namespace.add_owner(user)
+ end
+
+ after do
+ FileUtils.rm_rf(export_path, secure: true)
+ end
+
+ describe 'POST /projects/import' do
+ it 'schedules an import using a namespace' do
+ stub_import(namespace)
+
+ post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.id
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+
+ it 'schedules an import using the namespace path' do
+ stub_import(namespace)
+
+ post api('/projects/import', user), path: 'test-import', file: fixture_file_upload(file), namespace: namespace.full_path
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+
+ it 'schedules an import at the user namespace level' do
+ stub_import(user.namespace)
+
+ post api('/projects/import', user), path: 'test-import2', file: fixture_file_upload(file)
+
+ expect(response).to have_gitlab_http_status(201)
+ end
+
+ it 'schedules an import at the user namespace level' do
+ expect_any_instance_of(Project).not_to receive(:import_schedule)
+ expect(::Projects::CreateService).not_to receive(:new)
+
+ post api('/projects/import', user), namespace: 'nonexistent', path: 'test-import2', file: fixture_file_upload(file)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 Namespace Not Found')
+ end
+
+ it 'does not schedule an import if the user has no permission to the namespace' do
+ expect_any_instance_of(Project).not_to receive(:import_schedule)
+
+ post(api('/projects/import', create(:user)),
+ path: 'test-import3',
+ file: fixture_file_upload(file),
+ namespace: namespace.full_path)
+
+ expect(response).to have_gitlab_http_status(404)
+ expect(json_response['message']).to eq('404 Namespace Not Found')
+ end
+
+ it 'does not schedule an import if the user uploads no valid file' do
+ expect_any_instance_of(Project).not_to receive(:import_schedule)
+
+ post api('/projects/import', user), path: 'test-import3', file: './random/test'
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['error']).to eq('file is invalid')
+ end
+
+ def stub_import(namespace)
+ expect_any_instance_of(Project).to receive(:import_schedule)
+ expect(::Projects::CreateService).to receive(:new).with(user, hash_including(namespace_id: namespace.id)).and_call_original
+ end
+ end
+
+ describe 'GET /projects/:id/import' do
+ it 'returns the import status' do
+ project = create(:project, import_status: 'started')
+ project.add_master(user)
+
+ get api("/projects/#{project.id}/import", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to include('import_status' => 'started')
+ end
+
+ it 'returns the import status and the error if failed' do
+ project = create(:project, import_status: 'failed', import_error: 'error')
+ project.add_master(user)
+
+ get api("/projects/#{project.id}/import", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to include('import_status' => 'failed',
+ 'import_error' => 'error')
+ end
+ end
+end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index f11cd638d96..cee93f6ed14 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -7,7 +7,7 @@ describe API::Projects do
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, namespace: user.namespace) }
- let(:project2) { create(:project, path: 'project2', namespace: user.namespace) }
+ let(:project2) { create(:project, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
@@ -315,7 +315,7 @@ describe API::Projects do
context 'and with all query parameters' do
let!(:project5) { create(:project, :public, path: 'gitlab5', namespace: create(:namespace)) }
- let!(:project6) { create(:project, :public, path: 'project6', namespace: user.namespace) }
+ let!(:project6) { create(:project, :public, namespace: user.namespace) }
let!(:project7) { create(:project, :public, path: 'gitlab7', namespace: user.namespace) }
let!(:project8) { create(:project, path: 'gitlab8', namespace: user.namespace) }
let!(:project9) { create(:project, :public, path: 'gitlab9') }
@@ -460,7 +460,7 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(201)
project.each_pair do |k, v|
- next if %i[has_external_issue_tracker issues_enabled merge_requests_enabled wiki_enabled].include?(k)
+ next if %i[has_external_issue_tracker issues_enabled merge_requests_enabled wiki_enabled storage_version].include?(k)
expect(json_response[k.to_s]).to eq(v)
end
@@ -622,12 +622,8 @@ describe API::Projects do
end
describe 'POST /projects/user/:id' do
- before do
- expect(project).to be_persisted
- end
-
it 'creates new project without path but with name and return 201' do
- expect { post api("/projects/user/#{user.id}", admin), name: 'Foo Project' }.to change {Project.count}.by(1)
+ expect { post api("/projects/user/#{user.id}", admin), name: 'Foo Project' }.to change { Project.count }.by(1)
expect(response).to have_gitlab_http_status(201)
project = Project.last
@@ -666,8 +662,9 @@ describe API::Projects do
post api("/projects/user/#{user.id}", admin), project
expect(response).to have_gitlab_http_status(201)
+
project.each_pair do |k, v|
- next if %i[has_external_issue_tracker path].include?(k)
+ next if %i[has_external_issue_tracker path storage_version].include?(k)
expect(json_response[k.to_s]).to eq(v)
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index a0026c6e11c..9052a18c60b 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -180,6 +180,18 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end
+
+ context 'for milestones scope with group path as id' do
+ before do
+ another_project = create(:project, :public)
+ create(:milestone, project: project, title: 'awesome milestone')
+ create(:milestone, project: another_project, title: 'awesome milestone other project')
+
+ get api("/groups/#{CGI.escape(group.full_path)}/-/search", user), scope: 'milestones', search: 'awesome'
+ end
+
+ it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
+ end
end
end
@@ -283,7 +295,15 @@ describe API::Search do
get api("/projects/#{repo_project.id}/-/search", user), scope: 'commits', search: '498214de67004b1da3d820901307bed2a68a8ef6'
end
- it_behaves_like 'response is correct', schema: 'public_api/v4/commits'
+ it_behaves_like 'response is correct', schema: 'public_api/v4/commits_details'
+ end
+
+ context 'for commits scope with project path as id' do
+ before do
+ get api("/projects/#{CGI.escape(repo_project.full_path)}/-/search", user), scope: 'commits', search: '498214de67004b1da3d820901307bed2a68a8ef6'
+ end
+
+ it_behaves_like 'response is correct', schema: 'public_api/v4/commits_details'
end
context 'for blobs scope' do
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 74198c8eb4f..b3e253befc6 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -32,6 +32,27 @@ describe API::Snippets do
expect(json_response).to be_an Array
expect(json_response.size).to eq(0)
end
+
+ it 'returns 404 for non-authenticated' do
+ create(:personal_snippet, :internal)
+
+ get api("/snippets/")
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+
+ it 'does not return snippets related to a project with disable feature visibility' do
+ project = create(:project)
+ create(:project_member, project: project, user: user)
+ public_snippet = create(:personal_snippet, :public, author: user, project: project)
+ project.project_feature.update_attribute(:snippets_access_level, 0)
+
+ get api("/snippets/", user)
+
+ json_response.each do |snippet|
+ expect(snippet["id"]).not_to eq(public_snippet.id)
+ end
+ end
end
describe 'GET /snippets/public' do
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index fb3a33cadff..2ee8d150dc8 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -129,6 +129,12 @@ describe API::Todos do
post api("/todos/#{pending_1.id}/mark_as_done", john_doe)
end
+
+ it 'returns 404 if the todo does not belong to the current user' do
+ post api("/todos/#{pending_1.id}/mark_as_done", author_1)
+
+ expect(response.status).to eq(404)
+ end
end
end
diff --git a/spec/requests/api/v3/issues_spec.rb b/spec/requests/api/v3/issues_spec.rb
index 0dd6d673625..11b5469be7b 100644
--- a/spec/requests/api/v3/issues_spec.rb
+++ b/spec/requests/api/v3/issues_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe API::V3::Issues, :mailer do
+describe API::V3::Issues do
set(:user) { create(:user) }
set(:user2) { create(:user) }
set(:non_member) { create(:user) }
@@ -780,18 +780,6 @@ describe API::V3::Issues, :mailer do
expect(json_response['error']).to eq('confidential is invalid')
end
- it "sends notifications for subscribers of newly added labels" do
- label = project.labels.first
- label.toggle_subscription(user2, project)
-
- perform_enqueued_jobs do
- post v3_api("/projects/#{project.id}/issues", user),
- title: 'new issue', labels: label.title
- end
-
- should_email(user2)
- end
-
it "returns a 400 bad request if title not given" do
post v3_api("/projects/#{project.id}/issues", user), labels: 'label, label2'
@@ -1045,18 +1033,6 @@ describe API::V3::Issues, :mailer do
expect(json_response['labels']).to eq([label.title])
end
- it "sends notifications for subscribers of newly added labels when issue is updated" do
- label = create(:label, title: 'foo', color: '#FFAABB', project: project)
- label.toggle_subscription(user2, project)
-
- perform_enqueued_jobs do
- put v3_api("/projects/#{project.id}/issues/#{issue.id}", user),
- title: 'updated title', labels: label.title
- end
-
- should_email(user2)
- end
-
it 'removes all labels' do
put v3_api("/projects/#{project.id}/issues/#{issue.id}", user), labels: ''
@@ -1191,7 +1167,7 @@ describe API::V3::Issues, :mailer do
end
describe '/projects/:id/issues/:issue_id/move' do
- let!(:target_project) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace ) }
+ let!(:target_project) { create(:project, creator_id: user.id, namespace: user.namespace ) }
let!(:target_project2) { create(:project, creator_id: non_member.id, namespace: non_member.namespace ) }
it 'moves an issue' do
@@ -1242,7 +1218,7 @@ describe API::V3::Issues, :mailer do
context 'when source project does not exist' do
it 'returns 404 when trying to move an issue' do
- post v3_api("/projects/123/issues/#{issue.id}/move", user),
+ post v3_api("/projects/0/issues/#{issue.id}/move", user),
to_project_id: target_project.id
expect(response).to have_gitlab_http_status(404)
@@ -1253,7 +1229,7 @@ describe API::V3::Issues, :mailer do
context 'when target project does not exist' do
it 'returns 404 when trying to move an issue' do
post v3_api("/projects/#{project.id}/issues/#{issue.id}/move", user),
- to_project_id: 123
+ to_project_id: 0
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index 5d99d9495f3..4c25bd935c6 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -6,7 +6,7 @@ describe API::V3::Projects do
let(:user3) { create(:user) }
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
- let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
+ let(:project2) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
@@ -401,7 +401,7 @@ describe API::V3::Projects do
post v3_api('/projects', user), project
project.each_pair do |k, v|
- next if %i[has_external_issue_tracker issues_enabled merge_requests_enabled wiki_enabled].include?(k)
+ next if %i[storage_version has_external_issue_tracker issues_enabled merge_requests_enabled wiki_enabled].include?(k)
expect(json_response[k.to_s]).to eq(v)
end
@@ -545,7 +545,7 @@ describe API::V3::Projects do
expect(response).to have_gitlab_http_status(201)
project.each_pair do |k, v|
- next if %i[has_external_issue_tracker path].include?(k)
+ next if %i[storage_version has_external_issue_tracker path].include?(k)
expect(json_response[k.to_s]).to eq(v)
end
diff --git a/spec/requests/api/v3/todos_spec.rb b/spec/requests/api/v3/todos_spec.rb
index 53fd962272a..ea648e3917f 100644
--- a/spec/requests/api/v3/todos_spec.rb
+++ b/spec/requests/api/v3/todos_spec.rb
@@ -38,6 +38,12 @@ describe API::V3::Todos do
delete v3_api("/todos/#{pending_1.id}", john_doe)
end
+
+ it 'returns 404 if the todo does not belong to the current user' do
+ delete v3_api("/todos/#{pending_1.id}", author_1)
+
+ expect(response.status).to eq(404)
+ end
end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 2e2dccdafad..1b0a5eac9b0 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -150,7 +150,7 @@ describe 'Git HTTP requests' do
let(:path) { "/#{wiki.repository.full_path}.git" }
context "when the project is public" do
- let(:project) { create(:project, :repository, :public, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :public, :wiki_enabled) }
it_behaves_like 'pushes require Basic HTTP Authentication'
@@ -163,7 +163,7 @@ describe 'Git HTTP requests' do
download(path) do |response|
json_body = ActiveSupport::JSON.decode(response.body)
- expect(json_body['RepoPath']).to include(wiki.repository.full_path)
+ expect(json_body['RepoPath']).to include(wiki.repository.disk_path)
end
end
end
@@ -177,7 +177,7 @@ describe 'Git HTTP requests' do
end
context 'but the repo is disabled' do
- let(:project) { create(:project, :repository, :public, :repository_disabled, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :public, :repository_disabled, :wiki_enabled) }
it_behaves_like 'pulls are allowed'
it_behaves_like 'pushes are allowed'
@@ -198,7 +198,7 @@ describe 'Git HTTP requests' do
end
context "when the project is private" do
- let(:project) { create(:project, :repository, :private, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :private, :wiki_enabled) }
it_behaves_like 'pulls require Basic HTTP Authentication'
it_behaves_like 'pushes require Basic HTTP Authentication'
@@ -210,7 +210,7 @@ describe 'Git HTTP requests' do
end
context 'but the repo is disabled' do
- let(:project) { create(:project, :repository, :private, :repository_disabled, :wiki_enabled) }
+ let(:project) { create(:project, :wiki_repo, :private, :repository_disabled, :wiki_enabled) }
it 'allows clones' do
download(path, user: user.username, password: user.password) do |response|
@@ -597,7 +597,7 @@ describe 'Git HTTP requests' do
context "when a gitlab ci token is provided" do
let(:project) { create(:project, :repository) }
let(:build) { create(:ci_build, :running) }
- let(:other_project) { create(:project) }
+ let(:other_project) { create(:project, :repository) }
before do
build.update!(project: project) # can't associate it on factory create
@@ -648,10 +648,10 @@ describe 'Git HTTP requests' do
context 'when the repo does not exist' do
let(:project) { create(:project) }
- it 'rejects pulls with 403 Forbidden' do
+ it 'rejects pulls with 404 Not Found' do
clone_get path, env
- expect(response).to have_gitlab_http_status(:forbidden)
+ expect(response).to have_gitlab_http_status(:not_found)
expect(response.body).to eq(git_access_error(:no_repo))
end
end
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index 1a5ad9b04e4..6bed8e812c0 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -65,13 +65,23 @@ describe 'OpenID Connect requests' do
)
end
- let(:public_email) { build :email, email: 'public@example.com' }
- let(:private_email) { build :email, email: 'private@example.com' }
+ let!(:public_email) { build :email, email: 'public@example.com' }
+ let!(:private_email) { build :email, email: 'private@example.com' }
- it 'includes all user information' do
+ let!(:group1) { create :group }
+ let!(:group2) { create :group }
+ let!(:group3) { create :group, parent: group2 }
+ let!(:group4) { create :group, parent: group3 }
+
+ before do
+ group1.add_user(user, GroupMember::OWNER)
+ group3.add_user(user, Gitlab::Access::DEVELOPER)
+ end
+
+ it 'includes all user information and group memberships' do
request_user_info
- expect(json_response).to eq({
+ expect(json_response).to match(a_hash_including({
'sub' => hashed_subject,
'name' => 'Alice',
'nickname' => 'alice',
@@ -79,8 +89,13 @@ describe 'OpenID Connect requests' do
'email_verified' => true,
'website' => 'https://example.com',
'profile' => 'http://localhost/alice',
- 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png"
- })
+ 'picture' => "http://localhost/uploads/-/system/user/avatar/#{user.id}/dk.png",
+ 'groups' => anything
+ }))
+
+ expected_groups = [group1.full_path, group3.full_path]
+ expected_groups << group4.full_path if Group.supports_nested_groups?
+ expect(json_response['groups']).to match_array(expected_groups)
end
end
diff --git a/spec/requests/rack_attack_global_spec.rb b/spec/requests/rack_attack_global_spec.rb
index 0fec14d0cce..b18e922b063 100644
--- a/spec/requests/rack_attack_global_spec.rb
+++ b/spec/requests/rack_attack_global_spec.rb
@@ -22,6 +22,7 @@ describe 'Rack Attack global throttles' do
let(:url_that_does_not_require_authentication) { '/users/sign_in' }
let(:url_that_requires_authentication) { '/dashboard/snippets' }
+ let(:url_api_internal) { '/api/v4/internal/check' }
let(:api_partial_url) { '/todos' }
around do |example|
@@ -172,6 +173,15 @@ describe 'Rack Attack global throttles' do
get url_that_does_not_require_authentication
expect(response).to have_http_status 200
end
+
+ context 'when the request is to the api internal endpoints' do
+ it 'allows requests over the rate limit' do
+ (1 + requests_per_period).times do
+ get url_api_internal, secret_token: Gitlab::Shell.secret_token
+ expect(response).to have_http_status 200
+ end
+ end
+ end
end
context 'when the throttle is disabled' do
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 91aefa84d0e..56d025f0176 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -37,6 +37,22 @@ describe UsersController, "routing" do
it "to #calendar_activities" do
expect(get("/users/User/calendar_activities")).to route_to('users#calendar_activities', username: 'User')
end
+
+ describe 'redirect alias routes' do
+ include RSpec::Rails::RequestExampleGroup
+
+ it '/u/user1 redirects to /user1' do
+ expect(get("/u/user1")).to redirect_to('/user1')
+ end
+
+ it '/u/user1/groups redirects to /user1/groups' do
+ expect(get("/u/user1/groups")).to redirect_to('/users/user1/groups')
+ end
+
+ it '/u/user1/projects redirects to /user1/projects' do
+ expect(get("/u/user1/projects")).to redirect_to('/users/user1/projects')
+ end
+ end
end
# search GET /search(.:format) search#show
diff --git a/spec/services/chat_names/find_user_service_spec.rb b/spec/services/chat_names/find_user_service_spec.rb
index 79aaac3aeb6..5734b10109a 100644
--- a/spec/services/chat_names/find_user_service_spec.rb
+++ b/spec/services/chat_names/find_user_service_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe ChatNames::FindUserService do
+describe ChatNames::FindUserService, :clean_gitlab_redis_shared_state do
describe '#execute' do
let(:service) { create(:service) }
@@ -13,21 +13,30 @@ describe ChatNames::FindUserService do
context 'when existing user is requested' do
let(:params) { { team_id: chat_name.team_id, user_id: chat_name.chat_id } }
- it 'returns the existing user' do
- is_expected.to eq(user)
+ it 'returns the existing chat_name' do
+ is_expected.to eq(chat_name)
end
- it 'updates when last time chat name was used' do
+ it 'updates the last used timestamp if one is not already set' do
expect(chat_name.last_used_at).to be_nil
subject
- initial_last_used = chat_name.reload.last_used_at
- expect(initial_last_used).to be_present
+ expect(chat_name.reload.last_used_at).to be_present
+ end
+
+ it 'only updates an existing timestamp once within a certain time frame' do
+ service = described_class.new(service, params)
+
+ expect(chat_name.last_used_at).to be_nil
+
+ service.execute
+
+ time = chat_name.reload.last_used_at
- Timecop.travel(2.days.from_now) { described_class.new(service, params).execute }
+ service.execute
- expect(chat_name.reload.last_used_at).to be > initial_last_used
+ expect(chat_name.reload.last_used_at).to eq(time)
end
end
diff --git a/spec/services/ci/create_trace_artifact_service_spec.rb b/spec/services/ci/create_trace_artifact_service_spec.rb
index 847a88920fe..8c5e8e438c7 100644
--- a/spec/services/ci/create_trace_artifact_service_spec.rb
+++ b/spec/services/ci/create_trace_artifact_service_spec.rb
@@ -4,40 +4,60 @@ describe Ci::CreateTraceArtifactService do
describe '#execute' do
subject { described_class.new(nil, nil).execute(job) }
- let(:job) { create(:ci_build) }
-
context 'when the job does not have trace artifact' do
context 'when the job has a trace file' do
- before do
- allow_any_instance_of(Gitlab::Ci::Trace)
- .to receive(:default_path) { expand_fixture_path('trace/sample_trace') }
+ let!(:job) { create(:ci_build, :trace_live) }
+ let!(:legacy_path) { job.trace.read { |stream| return stream.path } }
+ let!(:legacy_checksum) { Digest::SHA256.file(legacy_path).hexdigest }
+ let(:new_path) { job.job_artifacts_trace.file.path }
+ let(:new_checksum) { Digest::SHA256.file(new_path).hexdigest }
- allow_any_instance_of(JobArtifactUploader).to receive(:move_to_cache) { false }
- allow_any_instance_of(JobArtifactUploader).to receive(:move_to_store) { false }
- end
+ it { expect(File.exist?(legacy_path)).to be_truthy }
it 'creates trace artifact' do
expect { subject }.to change { Ci::JobArtifact.count }.by(1)
- expect(job.job_artifacts_trace.read_attribute(:file)).to eq('sample_trace')
+ expect(File.exist?(legacy_path)).to be_falsy
+ expect(File.exist?(new_path)).to be_truthy
+ expect(new_checksum).to eq(legacy_checksum)
+ expect(job.job_artifacts_trace.file.exists?).to be_truthy
+ expect(job.job_artifacts_trace.file.filename).to eq('job.log')
end
- context 'when the job has already had trace artifact' do
+ context 'when failed to create trace artifact record' do
before do
- create(:ci_job_artifact, :trace, job: job)
+ # When ActiveRecord error happens
+ allow_any_instance_of(Ci::JobArtifact).to receive(:save).and_return(false)
+ allow_any_instance_of(Ci::JobArtifact).to receive_message_chain(:errors, :full_messages)
+ .and_return("Error")
+
+ subject rescue nil
+
+ job.reload
end
- it 'does not create trace artifact' do
- expect { subject }.not_to change { Ci::JobArtifact.count }
+ it 'keeps legacy trace and removes trace artifact' do
+ expect(File.exist?(legacy_path)).to be_truthy
+ expect(job.job_artifacts_trace).to be_nil
end
end
end
context 'when the job does not have a trace file' do
+ let!(:job) { create(:ci_build) }
+
it 'does not create trace artifact' do
expect { subject }.not_to change { Ci::JobArtifact.count }
end
end
end
+
+ context 'when the job has already had trace artifact' do
+ let!(:job) { create(:ci_build, :trace_artifact) }
+
+ it 'does not create trace artifact' do
+ expect { subject }.not_to change { Ci::JobArtifact.count }
+ end
+ end
end
end
diff --git a/spec/services/groups/destroy_service_spec.rb b/spec/services/groups/destroy_service_spec.rb
index ac4b9c02ba7..e8216abb08b 100644
--- a/spec/services/groups/destroy_service_spec.rb
+++ b/spec/services/groups/destroy_service_spec.rb
@@ -6,7 +6,7 @@ describe Groups::DestroyService do
let!(:user) { create(:user) }
let!(:group) { create(:group) }
let!(:nested_group) { create(:group, parent: group) }
- let!(:project) { create(:project, namespace: group) }
+ let!(:project) { create(:project, :legacy_storage, namespace: group) }
let!(:notification_setting) { create(:notification_setting, source: group)}
let(:gitlab_shell) { Gitlab::Shell.new }
let(:remove_path) { group.path + "+#{group.id}+deleted" }
@@ -141,7 +141,7 @@ describe Groups::DestroyService do
end
context 'legacy storage' do
- let!(:project) { create(:project, :empty_repo, namespace: group) }
+ let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: group) }
it 'removes repository' do
expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_falsey
@@ -149,7 +149,7 @@ describe Groups::DestroyService do
end
context 'hashed storage' do
- let!(:project) { create(:project, :hashed, :empty_repo, namespace: group) }
+ let!(:project) { create(:project, :empty_repo, namespace: group) }
it 'removes repository' do
expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_falsey
diff --git a/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb b/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb
new file mode 100644
index 00000000000..4e58179f45f
--- /dev/null
+++ b/spec/services/issues/fetch_referenced_merge_requests_service_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper.rb'
+
+describe Issues::FetchReferencedMergeRequestsService do
+ let(:project) { create(:project) }
+ let(:issue) { create(:issue, project: project) }
+ let(:other_project) { create(:project) }
+
+ let(:mr) { create(:merge_request, source_project: project, target_project: project, id: 2)}
+ let(:other_mr) { create(:merge_request, source_project: other_project, target_project: other_project, id: 1)}
+
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ context 'with mentioned merge requests' do
+ it 'returns a list of sorted merge requests' do
+ allow(issue).to receive(:referenced_merge_requests).with(user).and_return([other_mr, mr])
+
+ mrs, closed_by_mrs = service.execute(issue)
+
+ expect(mrs).to match_array([mr, other_mr])
+ expect(closed_by_mrs).to match_array([])
+ end
+ end
+
+ context 'with closed-by merge requests' do
+ it 'returns a list of sorted merge requests' do
+ allow(issue).to receive(:closed_by_merge_requests).with(user).and_return([other_mr, mr])
+
+ mrs, closed_by_mrs = service.execute(issue)
+
+ expect(mrs).to match_array([])
+ expect(closed_by_mrs).to match_array([mr, other_mr])
+ end
+ end
+end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index e91faea00b7..a9aee9e100f 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -232,6 +232,28 @@ describe Issues::MoveService do
end
end
+ context 'issue with assignee' do
+ let(:assignee) { create(:user) }
+
+ before do
+ old_issue.assignees = [assignee]
+ end
+
+ it 'preserves assignee with access to the new issue' do
+ new_project.add_reporter(assignee)
+
+ new_issue = move_service.execute(old_issue, new_project)
+
+ expect(new_issue.assignees).to eq([assignee])
+ end
+
+ it 'ignores assignee without access to the new issue' do
+ new_issue = move_service.execute(old_issue, new_project)
+
+ expect(new_issue.assignees).to be_empty
+ end
+ end
+
context 'notes with references' do
before do
create(:merge_request, source_project: old_project)
diff --git a/spec/services/labels/find_or_create_service_spec.rb b/spec/services/labels/find_or_create_service_spec.rb
index 78aa5d442e7..68d5660445a 100644
--- a/spec/services/labels/find_or_create_service_spec.rb
+++ b/spec/services/labels/find_or_create_service_spec.rb
@@ -15,47 +15,79 @@ describe Labels::FindOrCreateService do
context 'when acting on behalf of a specific user' do
let(:user) { create(:user) }
- subject(:service) { described_class.new(user, project, params) }
- before do
- project.add_developer(user)
- end
- context 'when label does not exist at group level' do
- it 'creates a new label at project level' do
- expect { service.execute }.to change(project.labels, :count).by(1)
+ context 'when finding labels on project level' do
+ subject(:service) { described_class.new(user, project, params) }
+
+ before do
+ project.add_developer(user)
end
- end
- context 'when label exists at group level' do
- it 'returns the group label' do
- group_label = create(:group_label, group: group, title: 'Security')
+ context 'when label does not exist at group level' do
+ it 'creates a new label at project level' do
+ expect { service.execute }.to change(project.labels, :count).by(1)
+ end
+ end
- expect(service.execute).to eq group_label
+ context 'when label exists at group level' do
+ it 'returns the group label' do
+ group_label = create(:group_label, group: group, title: 'Security')
+
+ expect(service.execute).to eq group_label
+ end
+ end
+
+ context 'when label exists at project level' do
+ it 'returns the project label' do
+ project_label = create(:label, project: project, title: 'Security')
+
+ expect(service.execute).to eq project_label
+ end
end
end
- context 'when label does not exist at group level' do
- it 'creates a new label at project leve' do
- expect { service.execute }.to change(project.labels, :count).by(1)
+ context 'when finding labels on group level' do
+ subject(:service) { described_class.new(user, group, params) }
+
+ before do
+ group.add_developer(user)
+ end
+
+ context 'when label does not exist at group level' do
+ it 'creates a new label at group level' do
+ expect { service.execute }.to change(group.labels, :count).by(1)
+ end
+ end
+
+ context 'when label exists at group level' do
+ it 'returns the group label' do
+ group_label = create(:group_label, group: group, title: 'Security')
+
+ expect(service.execute).to eq group_label
+ end
end
end
+ end
+
+ context 'when authorization is not required' do
+ context 'when finding labels on project level' do
+ subject(:service) { described_class.new(nil, project, params) }
- context 'when label exists at project level' do
it 'returns the project label' do
project_label = create(:label, project: project, title: 'Security')
- expect(service.execute).to eq project_label
+ expect(service.execute(skip_authorization: true)).to eq project_label
end
end
- end
- context 'when authorization is not required' do
- subject(:service) { described_class.new(nil, project, params) }
+ context 'when finding labels on group level' do
+ subject(:service) { described_class.new(nil, group, params) }
- it 'returns the project label' do
- project_label = create(:label, project: project, title: 'Security')
+ it 'returns the group label' do
+ group_label = create(:group_label, group: group, title: 'Security')
- expect(service.execute(skip_authorization: true)).to eq project_label
+ expect(service.execute(skip_authorization: true)).to eq group_label
+ end
end
end
end
diff --git a/spec/services/members/authorized_destroy_service_spec.rb b/spec/services/members/authorized_destroy_service_spec.rb
index 757c45708b9..9cf6f64a078 100644
--- a/spec/services/members/authorized_destroy_service_spec.rb
+++ b/spec/services/members/authorized_destroy_service_spec.rb
@@ -21,6 +21,15 @@ describe Members::AuthorizedDestroyService do
.to change { Member.count }.from(3).to(2)
end
+ it "doesn't destroy invited project member notification_settings" do
+ project.add_developer(member_user)
+
+ member = create :project_member, :invited, project: project
+
+ expect { described_class.new(member, member_user).execute }
+ .not_to change { NotificationSetting.count }
+ end
+
it 'destroys invited group member' do
group.add_developer(member_user)
@@ -29,38 +38,73 @@ describe Members::AuthorizedDestroyService do
expect { described_class.new(member, member_user).execute }
.to change { Member.count }.from(2).to(1)
end
+
+ it "doesn't destroy invited group member notification_settings" do
+ group.add_developer(member_user)
+
+ member = create :group_member, :invited, group: group
+
+ expect { described_class.new(member, member_user).execute }
+ .not_to change { NotificationSetting.count }
+ end
+ end
+
+ context 'Requested user' do
+ it "doesn't destroy member notification_settings" do
+ member = create(:project_member, user: member_user, requested_at: Time.now)
+
+ expect { described_class.new(member, member_user).execute }
+ .not_to change { NotificationSetting.count }
+ end
end
context 'Group member' do
- it "unassigns issues and merge requests" do
+ let(:member) { group.members.find_by(user_id: member_user.id) }
+
+ before do
group.add_developer(member_user)
+ end
+ it "unassigns issues and merge requests" do
issue = create :issue, project: group_project, assignees: [member_user]
create :issue, assignees: [member_user]
merge_request = create :merge_request, target_project: group_project, source_project: group_project, assignee: member_user
create :merge_request, target_project: project, source_project: project, assignee: member_user
- member = group.members.find_by(user_id: member_user.id)
-
expect { described_class.new(member, member_user).execute }
.to change { number_of_assigned_issuables(member_user) }.from(4).to(2)
expect(issue.reload.assignee_ids).to be_empty
expect(merge_request.reload.assignee_id).to be_nil
end
+
+ it 'destroys member notification_settings' do
+ group.add_developer(member_user)
+ member = group.members.find_by(user_id: member_user.id)
+
+ expect { described_class.new(member, member_user).execute }
+ .to change { member_user.notification_settings.count }.by(-1)
+ end
end
context 'Project member' do
- it "unassigns issues and merge requests" do
+ let(:member) { project.members.find_by(user_id: member_user.id) }
+
+ before do
project.add_developer(member_user)
+ end
+ it "unassigns issues and merge requests" do
create :issue, project: project, assignees: [member_user]
create :merge_request, target_project: project, source_project: project, assignee: member_user
- member = project.members.find_by(user_id: member_user.id)
-
expect { described_class.new(member, member_user).execute }
.to change { number_of_assigned_issuables(member_user) }.from(2).to(0)
end
+
+ it 'destroys member notification_settings' do
+ expect { described_class.new(member, member_user).execute }
+ .to change { member_user.notification_settings.count }.by(-1)
+ end
end
end
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index e56d335a7d6..3a935d98540 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
describe MergeRequests::BuildService do
+ using RSpec::Parameterized::TableSyntax
include RepoHelpers
let(:project) { create(:project, :repository) }
@@ -111,6 +112,7 @@ describe MergeRequests::BuildService do
context 'one commit in the diff' do
let(:commits) { Commit.decorate([commit_1], project) }
+ let(:commit_description) { commit_1.safe_message.split(/\n+/, 2).last }
before do
stub_compare
@@ -125,7 +127,7 @@ describe MergeRequests::BuildService do
end
it 'uses the description of the commit as the description of the merge request' do
- expect(merge_request.description).to eq(commit_1.safe_message.split(/\n+/, 2).last)
+ expect(merge_request.description).to eq(commit_description)
end
context 'merge request already has a description set' do
@@ -148,68 +150,32 @@ describe MergeRequests::BuildService do
end
end
- context 'branch starts with issue IID followed by a hyphen' do
- let(:source_branch) { "#{issue.iid}-fix-issue" }
-
- it 'appends "Closes #$issue-iid" to the description' do
- expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\n\nCloses ##{issue.iid}")
+ context 'when the source branch matches an issue' do
+ where(:issue_tracker, :source_branch, :closing_message) do
+ :jira | 'FOO-123-fix-issue' | 'Closes FOO-123'
+ :jira | 'fix-issue' | nil
+ :custom_issue_tracker | '123-fix-issue' | 'Closes #123'
+ :custom_issue_tracker | 'fix-issue' | nil
+ :internal | '123-fix-issue' | 'Closes #123'
+ :internal | 'fix-issue' | nil
end
- context 'merge request already has a description set' do
- let(:description) { 'Merge request description' }
-
- it 'appends "Closes #$issue-iid" to the description' do
- expect(merge_request.description).to eq("#{description}\n\nCloses ##{issue.iid}")
+ with_them do
+ before do
+ if issue_tracker == :internal
+ issue.update!(iid: 123)
+ else
+ create(:"#{issue_tracker}_service", project: project)
+ end
end
- end
- context 'commit has no description' do
- let(:commits) { Commit.decorate([commit_2], project) }
+ it 'appends the closing description' do
+ expected_description = [commit_description, closing_message].compact.join("\n\n")
- it 'sets the description to "Closes #$issue-iid"' do
- expect(merge_request.description).to eq("Closes ##{issue.iid}")
+ expect(merge_request.description).to eq(expected_description)
end
end
end
-
- context 'branch starts with numeric characters followed by a hyphen with no issue tracker' do
- let(:source_branch) { '12345-fix-issue' }
-
- before do
- allow(project).to receive(:external_issue_tracker).and_return(false)
- allow(project).to receive(:issues_enabled?).and_return(false)
- end
-
- it 'uses the title of the commit as the title of the merge request' do
- expect(merge_request.title).to eq(commit_1.safe_message.split("\n").first)
- end
-
- it 'uses the description of the commit as the description of the merge request' do
- commit_description = commit_1.safe_message.split(/\n+/, 2).last
-
- expect(merge_request.description).to eq("#{commit_description}")
- end
- end
-
- context 'branch starts with JIRA-formatted external issue IID followed by a hyphen' do
- let(:source_branch) { 'EXMPL-12345-fix-issue' }
-
- before do
- allow(project).to receive(:external_issue_tracker).and_return(true)
- allow(project).to receive(:issues_enabled?).and_return(false)
- allow(project).to receive(:external_issue_reference_pattern).and_return(IssueTrackerService.reference_pattern)
- end
-
- it 'uses the title of the commit as the title of the merge request' do
- expect(merge_request.title).to eq(commit_1.safe_message.split("\n").first)
- end
-
- it 'uses the description of the commit as the description of the merge request and appends the closes text' do
- commit_description = commit_1.safe_message.split(/\n+/, 2).last
-
- expect(merge_request.description).to eq("#{commit_description}\n\nCloses EXMPL-12345")
- end
- end
end
context 'more than one commit in the diff' do
@@ -239,80 +205,62 @@ describe MergeRequests::BuildService do
end
end
- context 'branch starts with GitLab issue IID followed by a hyphen' do
- let(:source_branch) { "#{issue.iid}-fix-issue" }
-
- it 'sets the title to: Resolves "$issue-title"' do
- expect(merge_request.title).to eq("Resolve \"#{issue.title}\"")
+ context 'when the source branch matches an issue' do
+ where(:issue_tracker, :source_branch, :title, :closing_message) do
+ :jira | 'FOO-123-fix-issue' | 'Resolve FOO-123 "Fix issue"' | 'Closes FOO-123'
+ :jira | 'fix-issue' | 'Fix issue' | nil
+ :custom_issue_tracker | '123-fix-issue' | 'Resolve #123 "Fix issue"' | 'Closes #123'
+ :custom_issue_tracker | 'fix-issue' | 'Fix issue' | nil
+ :internal | '123-fix-issue' | 'Resolve "A bug"' | 'Closes #123'
+ :internal | 'fix-issue' | 'Fix issue' | nil
+ :internal | '124-fix-issue' | '124 fix issue' | nil
end
- context 'when issue is not accessible to user' do
+ with_them do
before do
- project.team.truncate
- end
-
- it 'uses branch title as the merge request title' do
- expect(merge_request.title).to eq("#{issue.iid} fix issue")
+ if issue_tracker == :internal
+ issue.update!(iid: 123)
+ else
+ create(:"#{issue_tracker}_service", project: project)
+ end
end
- end
-
- context 'issue does not exist' do
- let(:source_branch) { "#{issue.iid.succ}-fix-issue" }
- it 'uses the title of the branch as the merge request title' do
- expect(merge_request.title).to eq("#{issue.iid.succ} fix issue")
+ it 'sets the correct title' do
+ expect(merge_request.title).to eq(title)
end
- end
-
- context 'issue is confidential' do
- let(:issue_confidential) { true }
- it 'uses the title of the branch as the merge request title' do
- expect(merge_request.title).to eq("#{issue.iid} fix issue")
+ it 'sets the closing description' do
+ expect(merge_request.description).to eq(closing_message)
end
end
end
- context 'branch starts with numeric characters followed by a hyphen with no issue tracker' do
- let(:source_branch) { '12345-fix-issue' }
+ context 'when the issue is not accessible to user' do
+ let(:source_branch) { "#{issue.iid}-fix-issue" }
before do
- allow(project).to receive(:external_issue_tracker).and_return(false)
- allow(project).to receive(:issues_enabled?).and_return(false)
+ project.team.truncate
end
- it 'sets the title to the humanized branch title' do
- expect(merge_request.title).to eq('12345 fix issue')
+ it 'uses branch title as the merge request title' do
+ expect(merge_request.title).to eq("#{issue.iid} fix issue")
end
- end
-
- context 'branch starts with JIRA-formatted external issue IID' do
- let(:source_branch) { 'EXMPL-12345' }
- before do
- allow(project).to receive(:external_issue_tracker).and_return(true)
- allow(project).to receive(:issues_enabled?).and_return(false)
- allow(project).to receive(:external_issue_reference_pattern).and_return(IssueTrackerService.reference_pattern)
+ it 'does not set a description' do
+ expect(merge_request.description).to be_nil
end
+ end
- it 'sets the title to the humanized branch title' do
- expect(merge_request.title).to eq('Resolve EXMPL-12345')
- end
+ context 'when the issue is confidential' do
+ let(:source_branch) { "#{issue.iid}-fix-issue" }
+ let(:issue_confidential) { true }
- it 'appends the closes text' do
- expect(merge_request.description).to eq('Closes EXMPL-12345')
+ it 'uses the title of the branch as the merge request title' do
+ expect(merge_request.title).to eq("#{issue.iid} fix issue")
end
- context 'followed by hyphenated text' do
- let(:source_branch) { 'EXMPL-12345-fix-issue' }
-
- it 'sets the title to the humanized branch title' do
- expect(merge_request.title).to eq('Resolve EXMPL-12345 "Fix issue"')
- end
-
- it 'appends the closes text' do
- expect(merge_request.description).to eq('Closes EXMPL-12345')
- end
+ it 'does not set a description' do
+ expect(merge_request.description).to be_nil
end
end
end
diff --git a/spec/services/merge_requests/create_from_issue_service_spec.rb b/spec/services/merge_requests/create_from_issue_service_spec.rb
index 75553afc033..38d84cf0ceb 100644
--- a/spec/services/merge_requests/create_from_issue_service_spec.rb
+++ b/spec/services/merge_requests/create_from_issue_service_spec.rb
@@ -24,7 +24,7 @@ describe MergeRequests::CreateFromIssueService do
end
it 'delegates issue search to IssuesFinder' do
- expect_any_instance_of(IssuesFinder).to receive(:execute).once.and_call_original
+ expect_any_instance_of(IssuesFinder).to receive(:find_by).once.and_call_original
described_class.new(project, user, issue_iid: -1).execute
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 35eb84e5e88..62fdf870090 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -1307,6 +1307,33 @@ describe NotificationService, :mailer do
end
describe 'GroupMember' do
+ let(:added_user) { create(:user) }
+
+ describe '#new_access_request' do
+ let(:master) { create(:user) }
+ let(:owner) { create(:user) }
+ let(:developer) { create(:user) }
+ let!(:group) do
+ create(:group, :public, :access_requestable) do |group|
+ group.add_owner(owner)
+ group.add_master(master)
+ group.add_developer(developer)
+ end
+ end
+
+ before do
+ reset_delivered_emails!
+ end
+
+ it 'sends notification to group owners_and_masters' do
+ group.request_access(added_user)
+
+ should_email(owner)
+ should_email(master)
+ should_not_email(developer)
+ end
+ end
+
describe '#decline_group_invite' do
let(:creator) { create(:user) }
let(:group) { create(:group) }
@@ -1328,18 +1355,9 @@ describe NotificationService, :mailer do
describe '#new_group_member' do
let(:group) { create(:group) }
- let(:added_user) { create(:user) }
-
- def create_member!
- GroupMember.create(
- group: group,
- user: added_user,
- access_level: Gitlab::Access::GUEST
- )
- end
it 'sends a notification' do
- create_member!
+ group.add_guest(added_user)
should_only_email(added_user)
end
@@ -1349,7 +1367,7 @@ describe NotificationService, :mailer do
end
it 'does not send a notification' do
- create_member!
+ group.add_guest(added_user)
should_not_email_anyone
end
end
@@ -1357,8 +1375,42 @@ describe NotificationService, :mailer do
end
describe 'ProjectMember' do
+ let(:project) { create(:project) }
+ set(:added_user) { create(:user) }
+
+ describe '#new_access_request' do
+ context 'for a project in a user namespace' do
+ let(:project) do
+ create(:project, :public, :access_requestable) do |project|
+ project.add_master(project.owner)
+ end
+ end
+
+ it 'sends notification to project owners_and_masters' do
+ project.request_access(added_user)
+
+ should_only_email(project.owner)
+ end
+ end
+
+ context 'for a project in a group' do
+ let(:group_owner) { create(:user) }
+ let(:group) { create(:group).tap { |g| g.add_owner(group_owner) } }
+ let!(:project) { create(:project, :public, :access_requestable, namespace: group) }
+
+ before do
+ reset_delivered_emails!
+ end
+
+ it 'sends notification to group owners_and_masters' do
+ project.request_access(added_user)
+
+ should_only_email(group_owner)
+ end
+ end
+ end
+
describe '#decline_group_invite' do
- let(:project) { create(:project) }
let(:member) { create(:user) }
before do
@@ -1375,19 +1427,12 @@ describe NotificationService, :mailer do
end
describe '#new_project_member' do
- let(:project) { create(:project) }
- let(:added_user) { create(:user) }
-
- def create_member!
- create(:project_member, user: added_user, project: project)
- end
-
it do
create_member!
should_only_email(added_user)
end
- describe 'when notifications are disabled' do
+ context 'when notifications are disabled' do
before do
create_global_setting_for(added_user, :disabled)
end
@@ -1398,6 +1443,10 @@ describe NotificationService, :mailer do
end
end
end
+
+ def create_member!
+ create(:project_member, user: added_user, project: project)
+ end
end
context 'guest user in private project' do
@@ -1629,6 +1678,78 @@ describe NotificationService, :mailer do
end
end
+ describe 'Pages domains' do
+ set(:project) { create(:project) }
+ set(:domain) { create(:pages_domain, project: project) }
+ set(:u_blocked) { create(:user, :blocked) }
+ set(:u_silence) { create_user_with_notification(:disabled, 'silent', project) }
+ set(:u_owner) { project.owner }
+ set(:u_master1) { create(:user) }
+ set(:u_master2) { create(:user) }
+ set(:u_developer) { create(:user) }
+
+ before do
+ project.add_master(u_blocked)
+ project.add_master(u_silence)
+ project.add_master(u_master1)
+ project.add_master(u_master2)
+ project.add_developer(u_developer)
+
+ reset_delivered_emails!
+ end
+
+ %i[
+ pages_domain_enabled
+ pages_domain_disabled
+ pages_domain_verification_succeeded
+ pages_domain_verification_failed
+ ].each do |sym|
+ describe "##{sym}" do
+ subject(:notify!) { notification.send(sym, domain) }
+
+ it 'emails current watching masters' do
+ expect(Notify).to receive(:"#{sym}_email").at_least(:once).and_call_original
+
+ notify!
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+
+ it 'emails nobody if the project is missing' do
+ domain.project = nil
+
+ notify!
+
+ should_not_email_anyone
+ end
+ end
+ end
+
+ describe '#pages_domain_verification_failed' do
+ it 'emails current watching masters' do
+ notification.pages_domain_verification_failed(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+
+ describe '#pages_domain_enabled' do
+ it 'emails current watching masters' do
+ notification.pages_domain_enabled(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+
+ describe '#pages_domain_disabled' do
+ it 'emails current watching masters' do
+ notification.pages_domain_disabled(domain)
+
+ should_only_email(u_master1, u_master2, u_owner)
+ end
+ end
+ end
+
def build_team(project)
@u_watcher = create_global_setting_for(create(:user), :watch)
@u_participating = create_global_setting_for(create(:user), :participating)
diff --git a/spec/services/projects/create_from_template_service_spec.rb b/spec/services/projects/create_from_template_service_spec.rb
index 9919ec254c6..609d678caea 100644
--- a/spec/services/projects/create_from_template_service_spec.rb
+++ b/spec/services/projects/create_from_template_service_spec.rb
@@ -4,8 +4,10 @@ describe Projects::CreateFromTemplateService do
let(:user) { create(:user) }
let(:project_params) do
{
- path: user.to_param,
- template_name: 'rails'
+ path: user.to_param,
+ template_name: 'rails',
+ description: 'project description',
+ visibility_level: Gitlab::VisibilityLevel::PRIVATE
}
end
@@ -22,5 +24,7 @@ describe Projects::CreateFromTemplateService do
expect(project).to be_saved
expect(project.scheduled?).to be(true)
+ expect(project.description).to match('project description')
+ expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
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 15699574b3a..fb6d7171ac3 100644
--- a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do
subject(:service) { described_class.new(project) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :legacy_storage) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
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 7b536cc05cb..747bd4529a0 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do
let(:gitlab_shell) { Gitlab::Shell.new }
- let(:project) { create(:project, :repository, :wiki_repo) }
+ let(:project) { create(:project, :legacy_storage, :repository, :wiki_repo) }
let(:service) { described_class.new(project) }
let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) }
diff --git a/spec/services/projects/hashed_storage_migration_service_spec.rb b/spec/services/projects/hashed_storage_migration_service_spec.rb
index 466f0b5d7c2..e8e18bb3ac0 100644
--- a/spec/services/projects/hashed_storage_migration_service_spec.rb
+++ b/spec/services/projects/hashed_storage_migration_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::HashedStorageMigrationService do
- let(:project) { create(:project, :empty_repo, :wiki_repo) }
+ let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
subject(:service) { described_class.new(project) }
describe '#execute' do
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index ef68742a463..ae0e22e3dc0 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::TransferService do
let(:gitlab_shell) { Gitlab::Shell.new }
let(:user) { create(:user) }
let(:group) { create(:group) }
- let(:project) { create(:project, :repository, namespace: user.namespace) }
+ let(:project) { create(:project, :repository, :legacy_storage, namespace: user.namespace) }
context 'namespace -> namespace' do
before do
@@ -214,7 +214,7 @@ describe Projects::TransferService do
end
context 'when hashed storage in use' do
- let(:hashed_project) { create(:project, :repository, :hashed, namespace: user.namespace) }
+ let(:hashed_project) { create(:project, :repository, namespace: user.namespace) }
before do
group.add_owner(user)
diff --git a/spec/services/projects/update_service_spec.rb b/spec/services/projects/update_service_spec.rb
index fc6aa713d6f..a0b97ceead9 100644
--- a/spec/services/projects/update_service_spec.rb
+++ b/spec/services/projects/update_service_spec.rb
@@ -150,6 +150,8 @@ describe Projects::UpdateService do
let(:repository_storage_path) { Gitlab.config.repositories.storages[repository_storage]['path'] }
context 'with legacy storage' do
+ let(:project) { create(:project, :legacy_storage, :repository, creator: user, namespace: user.namespace) }
+
before do
gitlab_shell.add_repository(repository_storage, "#{user.namespace.full_path}/existing")
end
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index ae160d104f1..f793f55e51b 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -522,6 +522,22 @@ describe QuickActions::InterpretService do
let(:issuable) { merge_request }
end
+ context 'only group milestones available' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :public, namespace: group) }
+ let(:milestone) { create(:milestone, group: group, title: '10.0') }
+
+ it_behaves_like 'milestone command' do
+ let(:content) { "/milestone %#{milestone.title}" }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'milestone command' do
+ let(:content) { "/milestone %#{milestone.title}" }
+ let(:issuable) { merge_request }
+ end
+ end
+
it_behaves_like 'remove_milestone command' do
let(:content) { '/remove_milestone' }
let(:issuable) { issue }
diff --git a/spec/services/search/snippet_service_spec.rb b/spec/services/search/snippet_service_spec.rb
index bc7885b03d9..8ad162ad66e 100644
--- a/spec/services/search/snippet_service_spec.rb
+++ b/spec/services/search/snippet_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Search::SnippetService do
let(:author) { create(:author) }
- let(:project) { create(:project) }
+ let(:project) { create(:project, :public) }
let!(:public_snippet) { create(:snippet, :public, content: 'password: XXX') }
let!(:internal_snippet) { create(:snippet, :internal, content: 'password: XXX') }
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index 5e6c24f5730..562b89e6767 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -943,7 +943,8 @@ describe TodoService do
described_class.new.mark_todos_as_done_by_ids(todo, john_doe)
- expect_any_instance_of(TodosFinder).not_to receive(:execute)
+ # Make sure no TodosFinder is inialized to perform counting
+ expect(TodosFinder).not_to receive(:new)
expect(john_doe.todos_done_count).to eq(1)
expect(john_doe.todos_pending_count).to eq(1)
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index bb3d73edf8e..11c75ddfcf8 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -173,7 +173,7 @@ describe Users::DestroyService do
end
context 'legacy storage' do
- let!(:project) { create(:project, :empty_repo, namespace: user.namespace) }
+ let!(:project) { create(:project, :empty_repo, :legacy_storage, namespace: user.namespace) }
it 'removes repository' do
expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_falsey
@@ -181,7 +181,7 @@ describe Users::DestroyService do
end
context 'hashed storage' do
- let!(:project) { create(:project, :empty_repo, :hashed, namespace: user.namespace) }
+ let!(:project) { create(:project, :empty_repo, namespace: user.namespace) }
it 'removes repository' do
expect(gitlab_shell.exists?(project.repository_storage_path, "#{project.disk_path}.git")).to be_falsey
diff --git a/spec/services/verify_pages_domain_service_spec.rb b/spec/services/verify_pages_domain_service_spec.rb
new file mode 100644
index 00000000000..576db1dde2d
--- /dev/null
+++ b/spec/services/verify_pages_domain_service_spec.rb
@@ -0,0 +1,270 @@
+require 'spec_helper'
+
+describe VerifyPagesDomainService do
+ using RSpec::Parameterized::TableSyntax
+ include EmailHelpers
+
+ let(:error_status) { { status: :error, message: "Couldn't verify #{domain.domain}" } }
+
+ subject(:service) { described_class.new(domain) }
+
+ describe '#execute' do
+ context 'verification code recognition (verified domain)' do
+ where(:domain_sym, :code_sym) do
+ :domain | :verification_code
+ :domain | :keyed_verification_code
+
+ :verification_domain | :verification_code
+ :verification_domain | :keyed_verification_code
+ end
+
+ with_them do
+ set(:domain) { create(:pages_domain) }
+
+ let(:domain_name) { domain.send(domain_sym) }
+ let(:verification_code) { domain.send(code_sym) }
+
+ it 'verifies and enables the domain' do
+ stub_resolver(domain_name => ['something else', verification_code])
+
+ expect(service.execute).to eq(status: :success)
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'verifies and enables when the code is contained partway through a TXT record' do
+ stub_resolver(domain_name => "something #{verification_code} else")
+
+ expect(service.execute).to eq(status: :success)
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'does not verify when the code is not present' do
+ stub_resolver(domain_name => 'something else')
+
+ expect(service.execute).to eq(error_status)
+
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+ end
+
+ context 'verified domain' do
+ set(:domain) { create(:pages_domain) }
+
+ it 'unverifies (but does not disable) when the right code is not present' do
+ stub_resolver(domain.domain => 'something else')
+
+ expect(service.execute).to eq(error_status)
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'unverifies (but does not disable) when no records are present' do
+ stub_resolver
+
+ expect(service.execute).to eq(error_status)
+ expect(domain).not_to be_verified
+ expect(domain).to be_enabled
+ end
+ end
+
+ context 'expired domain' do
+ set(:domain) { create(:pages_domain, :expired) }
+
+ it 'verifies and enables when the right code is present' do
+ stub_resolver(domain.domain => domain.keyed_verification_code)
+
+ expect(service.execute).to eq(status: :success)
+
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'disables when the right code is not present' do
+ error_status[:message] += '. It is now disabled.'
+
+ stub_resolver
+
+ expect(service.execute).to eq(error_status)
+
+ expect(domain).not_to be_verified
+ expect(domain).not_to be_enabled
+ end
+ end
+ end
+
+ context 'timeout behaviour' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'sets a timeout on the DNS query' do
+ expect(stub_resolver).to receive(:timeouts=).with(described_class::RESOLVER_TIMEOUT_SECONDS)
+
+ service.execute
+ end
+ end
+
+ context 'email notifications' do
+ let(:notification_service) { instance_double('NotificationService') }
+
+ where(:factory, :verification_succeeds, :expected_notification) do
+ nil | true | nil
+ nil | false | :verification_failed
+ :reverify | true | nil
+ :reverify | false | :verification_failed
+ :unverified | true | :verification_succeeded
+ :unverified | false | nil
+ :expired | true | nil
+ :expired | false | :disabled
+ :disabled | true | :enabled
+ :disabled | false | nil
+ end
+
+ with_them do
+ let(:domain) { create(:pages_domain, *[factory].compact) }
+
+ before do
+ allow(service).to receive(:notification_service) { notification_service }
+
+ if verification_succeeds
+ stub_resolver(domain.domain => domain.verification_code)
+ else
+ stub_resolver
+ end
+ end
+
+ it 'sends a notification if appropriate' do
+ if expected_notification
+ expect(notification_service).to receive(:"pages_domain_#{expected_notification}").with(domain)
+ end
+
+ service.execute
+ end
+ end
+
+ context 'pages verification disabled' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ before do
+ stub_application_setting(pages_domain_verification_enabled: false)
+ allow(service).to receive(:notification_service) { notification_service }
+ end
+
+ it 'skips email notifications' do
+ expect(notification_service).not_to receive(:pages_domain_enabled)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'pages configuration updates' do
+ context 'enabling a disabled domain' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ it 'schedules an update' do
+ stub_resolver(domain.domain => domain.verification_code)
+
+ expect(domain).to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'verifying an enabled domain' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'schedules an update' do
+ stub_resolver(domain.domain => domain.verification_code)
+
+ expect(domain).not_to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'disabling an expired domain' do
+ let(:domain) { create(:pages_domain, :expired) }
+
+ it 'schedules an update' do
+ stub_resolver
+
+ expect(domain).to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+
+ context 'failing to verify a disabled domain' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ it 'does not schedule an update' do
+ stub_resolver
+
+ expect(domain).not_to receive(:update_daemon)
+
+ service.execute
+ end
+ end
+ end
+
+ context 'no verification code' do
+ let(:domain) { create(:pages_domain) }
+
+ it 'returns an error' do
+ domain.verification_code = ''
+
+ disallow_resolver!
+
+ expect(service.execute).to eq(status: :error, message: "No verification code set for #{domain.domain}")
+ end
+ end
+
+ context 'pages domain verification is disabled' do
+ let(:domain) { create(:pages_domain, :disabled) }
+
+ before do
+ stub_application_setting(pages_domain_verification_enabled: false)
+ end
+
+ it 'extends domain validity by unconditionally reverifying' do
+ disallow_resolver!
+
+ service.execute
+
+ expect(domain).to be_verified
+ expect(domain).to be_enabled
+ end
+
+ it 'does not shorten any grace period' do
+ grace = Time.now + 1.year
+ domain.update!(enabled_until: grace)
+ disallow_resolver!
+
+ service.execute
+
+ expect(domain.enabled_until).to be_like_time(grace)
+ end
+ end
+ end
+
+ def disallow_resolver!
+ expect(Resolv::DNS).not_to receive(:open)
+ end
+
+ def stub_resolver(stubbed_lookups = {})
+ resolver = instance_double('Resolv::DNS')
+ allow(resolver).to receive(:timeouts=)
+
+ expect(Resolv::DNS).to receive(:open).and_yield(resolver)
+
+ allow(resolver).to receive(:getresources) { [] }
+ stubbed_lookups.each do |domain, records|
+ records = Array(records).map { |txt| Resolv::DNS::Resource::IN::TXT.new(txt) }
+ allow(resolver).to receive(:getresources).with(domain, Resolv::DNS::Resource::IN::TXT) { records }
+ end
+
+ resolver
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 85de0a14631..c0f3366fb52 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -154,6 +154,22 @@ RSpec.configure do |config|
Sidekiq.redis(&:flushall)
end
+ # The :each scope runs "inside" the example, so this hook ensures the DB is in the
+ # correct state before any examples' before hooks are called. This prevents a
+ # problem where `ScheduleIssuesClosedAtTypeChange` (or any migration that depends
+ # on background migrations being run inline during test setup) can be broken by
+ # altering Sidekiq behavior in an unrelated spec like so:
+ #
+ # around do |example|
+ # Sidekiq::Testing.fake! do
+ # example.run
+ # end
+ # end
+ config.before(:context, :migration) do
+ schema_migrate_down!
+ end
+
+ # Each example may call `migrate!`, so we must ensure we are migrated down every time
config.before(:each, :migration) do
schema_migrate_down!
end
@@ -169,6 +185,14 @@ RSpec.configure do |config|
config.around(:each, :postgresql) do |example|
example.run if Gitlab::Database.postgresql?
end
+
+ # This makes sure the `ApplicationController#can?` method is stubbed with the
+ # original implementation for all view specs.
+ config.before(:each, type: :view) do
+ allow(view).to receive(:can?) do |*args|
+ Ability.allowed?(*args)
+ end
+ end
end
# add simpler way to match asset paths containing digest strings
diff --git a/spec/support/capybara.rb b/spec/support/capybara.rb
index 5189c57b7db..8603b7f3e2c 100644
--- a/spec/support/capybara.rb
+++ b/spec/support/capybara.rb
@@ -78,8 +78,10 @@ RSpec.configure do |config|
end
config.after(:example, :js) do |example|
- # prevent localstorage from introducing side effects based on test order
- execute_script("localStorage.clear();")
+ # prevent localStorage from introducing side effects based on test order
+ unless ['', 'about:blank', 'data:,'].include? Capybara.current_session.driver.browser.current_url
+ execute_script("localStorage.clear();")
+ end
# capybara/rspec already calls Capybara.reset_sessions! in an `after` hook,
# but `block_and_wait_for_requests_complete` is called before it so by
diff --git a/spec/support/controllers/githubish_import_controller_shared_examples.rb b/spec/support/controllers/githubish_import_controller_shared_examples.rb
index a0839eefe6c..3321f920666 100644
--- a/spec/support/controllers/githubish_import_controller_shared_examples.rb
+++ b/spec/support/controllers/githubish_import_controller_shared_examples.rb
@@ -92,6 +92,7 @@ end
shared_examples 'a GitHub-ish import controller: POST create' do
let(:user) { create(:user) }
+ let(:project) { create(:project) }
let(:provider_username) { user.username }
let(:provider_user) { OpenStruct.new(login: provider_username) }
let(:provider_repo) do
@@ -107,14 +108,34 @@ shared_examples 'a GitHub-ish import controller: POST create' do
assign_session_token(provider)
end
+ it 'returns 200 response when the project is imported successfully' do
+ allow(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: project))
+
+ post :create, format: :json
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns 422 response when the project could not be imported' do
+ allow(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: build(:project)))
+
+ post :create, format: :json
+
+ expect(response).to have_gitlab_http_status(422)
+ end
+
context "when the repository owner is the provider user" do
context "when the provider user and GitLab user's usernames match" do
it "takes the current user's namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -124,9 +145,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the current user's namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -151,9 +172,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it "takes the existing namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo.name, existing_namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
@@ -163,9 +184,9 @@ shared_examples 'a GitHub-ish import controller: POST create' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -174,17 +195,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
context "when current user can create namespaces" do
it "creates the namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).and_return(double(execute: true))
+ .to receive(:new).and_return(double(execute: project))
- expect { post :create, target_namespace: provider_repo.name, format: :js }.to change(Namespace, :count).by(1)
+ expect { post :create, target_namespace: provider_repo.name, format: :json }.to change(Namespace, :count).by(1)
end
it "takes the new namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo.name, an_instance_of(Group), user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, target_namespace: provider_repo.name, format: :js
+ post :create, target_namespace: provider_repo.name, format: :json
end
end
@@ -195,17 +216,17 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it "doesn't create the namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
- .to receive(:new).and_return(double(execute: true))
+ .to receive(:new).and_return(double(execute: project))
- expect { post :create, format: :js }.not_to change(Namespace, :count)
+ expect { post :create, format: :json }.not_to change(Namespace, :count)
end
it "takes the current user's namespace" do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, provider_repo.name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, format: :js
+ post :create, format: :json
end
end
end
@@ -221,21 +242,21 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, test_namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :js }
+ post :create, { target_namespace: test_namespace.name, new_name: test_name, format: :json }
end
it 'takes the selected name and default namespace' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { new_name: test_name, format: :js }
+ post :create, { new_name: test_name, format: :json }
end
end
- context 'user has chosen an existing nested namespace and name for the project' do
+ context 'user has chosen an existing nested namespace and name for the project', :postgresql do
let(:parent_namespace) { create(:group, name: 'foo', owner: user) }
let(:nested_namespace) { create(:group, name: 'bar', parent: parent_namespace) }
let(:test_name) { 'test_name' }
@@ -247,63 +268,124 @@ shared_examples 'a GitHub-ish import controller: POST create' do
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, nested_namespace, user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :js }
+ post :create, { target_namespace: nested_namespace.full_path, new_name: test_name, format: :json }
end
end
- context 'user has chosen a non-existent nested namespaces and name for the project' do
+ context 'user has chosen a non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :json }
end
it 'creates the namespaces' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js } }
+ expect { post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :json } }
.to change { Namespace.count }.by(2)
end
it 'new namespace has the right parent' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :js }
+ post :create, { target_namespace: 'foo/bar', new_name: test_name, format: :json }
expect(Namespace.find_by_path_or_name('bar').parent.path).to eq('foo')
end
end
- context 'user has chosen existent and non-existent nested namespaces and name for the project' do
+ context 'user has chosen existent and non-existent nested namespaces and name for the project', :postgresql do
let(:test_name) { 'test_name' }
let!(:parent_namespace) { create(:group, name: 'foo', owner: user) }
+ before do
+ parent_namespace.add_owner(user)
+ end
+
it 'takes the selected namespace and name' do
expect(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
+ post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :json }
end
it 'creates the namespaces' do
allow(Gitlab::LegacyGithubImport::ProjectCreator)
.to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
- .and_return(double(execute: true))
+ .and_return(double(execute: project))
- expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
+ expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :json } }
.to change { Namespace.count }.by(2)
end
+
+ it 'does not create a new namespace under the user namespace' do
+ expect(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: build_stubbed(:project)))
+
+ expect { post :create, { target_namespace: "#{user.namespace_path}/test_group", new_name: test_name, format: :js } }
+ .not_to change { Namespace.count }
+ end
+ end
+
+ context 'user cannot create a subgroup inside a group is not a member of' do
+ let(:test_name) { 'test_name' }
+ let!(:parent_namespace) { create(:group, name: 'foo') }
+
+ it 'does not take the selected namespace and name' do
+ expect(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, user.namespace, user, access_params, type: provider)
+ .and_return(double(execute: build_stubbed(:project)))
+
+ post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js }
+ end
+
+ it 'does not create the namespaces' do
+ allow(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, kind_of(Namespace), user, access_params, type: provider)
+ .and_return(double(execute: build_stubbed(:project)))
+
+ expect { post :create, { target_namespace: 'foo/foobar/bar', new_name: test_name, format: :js } }
+ .not_to change { Namespace.count }
+ end
+ end
+
+ context 'user can use a group without having permissions to create a group' do
+ let(:test_name) { 'test_name' }
+ let!(:group) { create(:group, name: 'foo') }
+
+ it 'takes the selected namespace and name' do
+ group.add_owner(user)
+ user.update!(can_create_group: false)
+
+ expect(Gitlab::LegacyGithubImport::ProjectCreator)
+ .to receive(:new).with(provider_repo, test_name, group, user, access_params, type: provider)
+ .and_return(double(execute: build_stubbed(:project)))
+
+ post :create, { target_namespace: 'foo', new_name: test_name, format: :js }
+ end
+ end
+
+ context 'when user can not create projects in the chosen namespace' do
+ it 'returns 422 response' do
+ other_namespace = create(:group, name: 'other_namespace')
+
+ post :create, { target_namespace: other_namespace.name, format: :json }
+
+ expect(response).to have_gitlab_http_status(422)
+ end
end
end
end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 1809ae1d141..5edc5de2a09 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -28,11 +28,11 @@ RSpec.configure do |config|
end
config.before(:each, :js) do
- DatabaseCleaner.strategy = :deletion
+ DatabaseCleaner.strategy = :deletion, { cache_tables: false }
end
config.before(:each, :delete) do
- DatabaseCleaner.strategy = :deletion
+ DatabaseCleaner.strategy = :deletion, { cache_tables: false }
end
config.before(:each, :migration) do
diff --git a/spec/support/factory_girl.rb b/spec/support/factory_bot.rb
index c7890e49c66..c7890e49c66 100644
--- a/spec/support/factory_girl.rb
+++ b/spec/support/factory_bot.rb
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index 83bf06b6727..f7f851eb1eb 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -41,13 +41,13 @@ shared_examples 'variable list' do
end
end
- it 'adds new unprotected variable' 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('.ci-variable-protected-item .js-project-feature-toggle').click
- expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
+ expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end
click_button('Save variables')
@@ -59,7 +59,7 @@ shared_examples 'variable list' do
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-protected', visible: false).value).to eq('false')
+ expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end
end
@@ -143,7 +143,6 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('unprotected_key')
find('.js-ci-variable-input-value').set('unprotected_value')
- find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
end
@@ -178,6 +177,7 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('protected_key')
find('.js-ci-variable-input-value').set('protected_value')
+ find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end
@@ -261,9 +261,11 @@ shared_examples 'variable list' do
click_button('Save variables')
wait_for_requests
+ expect(all('.js-ci-variable-list-section .js-ci-variable-error-box ul li').count).to eq(1)
+
# We check the first row because it re-sorts to alphabetical order on refresh
page.within('.js-ci-variable-list-section') do
- expect(find('.js-ci-variable-error-box')).to have_content('Validation failed Variables Duplicate variables: samekey')
+ expect(find('.js-ci-variable-error-box')).to have_content(/Validation failed Variables have duplicate values \(.+\)/)
end
end
end
diff --git a/spec/support/fixture_helpers.rb b/spec/support/fixture_helpers.rb
index 128aaaf25fe..8854382dc6b 100644
--- a/spec/support/fixture_helpers.rb
+++ b/spec/support/fixture_helpers.rb
@@ -1,12 +1,12 @@
module FixtureHelpers
- def fixture_file(filename)
+ def fixture_file(filename, dir: '')
return '' if filename.blank?
- File.read(expand_fixture_path(filename))
+ File.read(expand_fixture_path(filename, dir: dir))
end
- def expand_fixture_path(filename)
- File.expand_path(Rails.root.join('spec/fixtures/', filename))
+ def expand_fixture_path(filename, dir: '')
+ File.expand_path(Rails.root.join(dir, 'spec', 'fixtures', filename))
end
end
diff --git a/spec/support/migrations_helpers.rb b/spec/support/migrations_helpers.rb
index ba4a1bee089..6bf976a2cf9 100644
--- a/spec/support/migrations_helpers.rb
+++ b/spec/support/migrations_helpers.rb
@@ -25,14 +25,19 @@ module MigrationsHelpers
clear_schema_cache!
# Reset column information for the most offending classes **after** we
- # migrated the schema up, otherwise, column information could be outdated
- ActiveRecord::Base.descendants.each { |klass| klass.reset_column_information }
+ # migrated the schema up, otherwise, column information could be
+ # outdated. We have a separate method for this so we can override it in EE.
+ ActiveRecord::Base.descendants.each(&method(:reset_column_information))
# Without that, we get errors because of missing attributes, e.g.
# super: no superclass method `elasticsearch_indexing' for #<ApplicationSetting:0x00007f85628508d8>
ApplicationSetting.define_attribute_methods
end
+ def reset_column_information(klass)
+ klass.reset_column_information
+ end
+
def previous_migration
migrations.each_cons(2) do |previous, migration|
break previous if migration.name == described_class.name
@@ -40,7 +45,13 @@ module MigrationsHelpers
end
def migration_schema_version
- self.class.metadata[:schema] || previous_migration.version
+ metadata_schema = self.class.metadata[:schema]
+
+ if metadata_schema == :latest
+ migrations.last.version
+ else
+ metadata_schema || previous_migration.version
+ end
end
def schema_migrate_down!
@@ -53,6 +64,8 @@ module MigrationsHelpers
end
def schema_migrate_up!
+ reset_column_in_all_models
+
disable_migrations_output do
ActiveRecord::Migrator.migrate(migrations_paths)
end
diff --git a/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
new file mode 100644
index 00000000000..016bcfa9b1b
--- /dev/null
+++ b/spec/support/migrations_helpers/track_untracked_uploads_helpers.rb
@@ -0,0 +1,128 @@
+module MigrationsHelpers
+ module TrackUntrackedUploadsHelpers
+ PUBLIC_DIR = File.join(Rails.root, 'tmp', 'tests', 'public')
+ UPLOADS_DIR = File.join(PUBLIC_DIR, 'uploads')
+ SYSTEM_DIR = File.join(UPLOADS_DIR, '-', 'system')
+ UPLOAD_FILENAME = 'image.png'.freeze
+ FIXTURE_FILE_PATH = File.join(Rails.root, 'spec', 'fixtures', 'dk.png')
+ FIXTURE_CHECKSUM = 'b804383982bb89b00e828e3f44c038cc991d3d1768009fc39ba8e2c081b9fb75'.freeze
+
+ def create_or_update_appearance(logo: false, header_logo: false)
+ appearance = appearances.first_or_create(title: 'foo', description: 'bar', logo: (UPLOAD_FILENAME if logo), header_logo: (UPLOAD_FILENAME if header_logo))
+
+ add_upload(appearance, 'Appearance', 'logo', 'AttachmentUploader') if logo
+ add_upload(appearance, 'Appearance', 'header_logo', 'AttachmentUploader') if header_logo
+
+ appearance
+ end
+
+ def create_group(avatar: false)
+ index = unique_index(:group)
+ group = namespaces.create(name: "group#{index}", path: "group#{index}", avatar: (UPLOAD_FILENAME if avatar))
+
+ add_upload(group, 'Group', 'avatar', 'AvatarUploader') if avatar
+
+ group
+ end
+
+ def create_note(attachment: false)
+ note = notes.create(attachment: (UPLOAD_FILENAME if attachment))
+
+ add_upload(note, 'Note', 'attachment', 'AttachmentUploader') if attachment
+
+ note
+ end
+
+ def create_project(avatar: false)
+ group = create_group
+ project = projects.create(namespace_id: group.id, path: "project#{unique_index(:project)}", avatar: (UPLOAD_FILENAME if avatar))
+ routes.create(path: "#{group.path}/#{project.path}", source_id: project.id, source_type: 'Project') # so Project.find_by_full_path works
+
+ add_upload(project, 'Project', 'avatar', 'AvatarUploader') if avatar
+
+ project
+ end
+
+ def create_user(avatar: false)
+ user = users.create(email: "foo#{unique_index(:user)}@bar.com", avatar: (UPLOAD_FILENAME if avatar), projects_limit: 100)
+
+ add_upload(user, 'User', 'avatar', 'AvatarUploader') if avatar
+
+ user
+ end
+
+ def unique_index(name = :unnamed)
+ @unique_index ||= {}
+ @unique_index[name] ||= 0
+ @unique_index[name] += 1
+ end
+
+ def add_upload(model, model_type, attachment_type, uploader)
+ file_path = upload_file_path(model, model_type, attachment_type)
+ path_relative_to_public = file_path.sub("#{PUBLIC_DIR}/", '')
+ create_file(file_path)
+
+ uploads.create!(
+ size: 1062,
+ path: path_relative_to_public,
+ model_id: model.id,
+ model_type: model_type == 'Group' ? 'Namespace' : model_type,
+ uploader: uploader,
+ checksum: FIXTURE_CHECKSUM
+ )
+ end
+
+ def add_markdown_attachment(project, hashed_storage: false)
+ project_dir = hashed_storage ? hashed_project_uploads_dir(project) : legacy_project_uploads_dir(project)
+ attachment_dir = File.join(project_dir, SecureRandom.hex)
+ attachment_file_path = File.join(attachment_dir, UPLOAD_FILENAME)
+ project_attachment_path_relative_to_project = attachment_file_path.sub("#{project_dir}/", '')
+ create_file(attachment_file_path)
+
+ uploads.create!(
+ size: 1062,
+ path: project_attachment_path_relative_to_project,
+ model_id: project.id,
+ model_type: 'Project',
+ uploader: 'FileUploader',
+ checksum: FIXTURE_CHECKSUM
+ )
+ end
+
+ def legacy_project_uploads_dir(project)
+ namespace = namespaces.find_by(id: project.namespace_id)
+ File.join(UPLOADS_DIR, namespace.path, project.path)
+ end
+
+ def hashed_project_uploads_dir(project)
+ File.join(UPLOADS_DIR, '@hashed', 'aa', 'aaaaaaaaaaaa')
+ end
+
+ def upload_file_path(model, model_type, attachment_type)
+ dir = File.join(upload_dir(model_type.downcase, attachment_type.to_s), model.id.to_s)
+ File.join(dir, UPLOAD_FILENAME)
+ end
+
+ def upload_dir(model_type, attachment_type)
+ File.join(SYSTEM_DIR, model_type, attachment_type)
+ end
+
+ def create_file(path)
+ File.delete(path) if File.exist?(path)
+ FileUtils.mkdir_p(File.dirname(path))
+ FileUtils.cp(FIXTURE_FILE_PATH, path)
+ end
+
+ def get_uploads(model, model_type)
+ uploads.where(model_type: model_type, model_id: model.id)
+ end
+
+ def get_full_path(project)
+ routes.find_by(source_id: project.id, source_type: 'Project').path
+ end
+
+ def ensure_temporary_tracking_table_exists
+ Gitlab::BackgroundMigration::PrepareUntrackedUploads.new.send(:ensure_temporary_tracking_table_exists)
+ end
+ end
+end
diff --git a/spec/support/prometheus/additional_metrics_shared_examples.rb b/spec/support/prometheus/additional_metrics_shared_examples.rb
index dbbd4ad4d40..c7c3346d39e 100644
--- a/spec/support/prometheus/additional_metrics_shared_examples.rb
+++ b/spec/support/prometheus/additional_metrics_shared_examples.rb
@@ -12,11 +12,12 @@ RSpec.shared_examples 'additional metrics query' do
let(:client) { double('prometheus_client') }
let(:query_result) { described_class.new(client).query(*query_params) }
- let(:environment) { create(:environment, slug: 'environment-slug') }
+ let(:project) { create(:project) }
+ let(:environment) { create(:environment, slug: 'environment-slug', project: project) }
before do
allow(client).to receive(:label_values).and_return(metric_names)
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group(metrics: [simple_metric])])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group(metrics: [simple_metric])])
end
context 'metrics query context' do
@@ -24,13 +25,14 @@ RSpec.shared_examples 'additional metrics query' do
shared_examples 'query context containing environment slug and filter' do
it 'contains ci_environment_slug' do
- expect(subject).to receive(:query_metrics).with(hash_including(ci_environment_slug: environment.slug))
+ expect(subject).to receive(:query_metrics).with(project, hash_including(ci_environment_slug: environment.slug))
subject.query(*query_params)
end
it 'contains environment filter' do
expect(subject).to receive(:query_metrics).with(
+ project,
hash_including(
environment_filter: "container_name!=\"POD\",environment=\"#{environment.slug}\""
)
@@ -48,7 +50,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains kube_namespace' do
- expect(subject).to receive(:query_metrics).with(hash_including(kube_namespace: kube_namespace))
+ expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: kube_namespace))
subject.query(*query_params)
end
@@ -72,7 +74,7 @@ RSpec.shared_examples 'additional metrics query' do
it_behaves_like 'query context containing environment slug and filter'
it 'query context contains empty kube_namespace' do
- expect(subject).to receive(:query_metrics).with(hash_including(kube_namespace: ''))
+ expect(subject).to receive(:query_metrics).with(project, hash_including(kube_namespace: ''))
subject.query(*query_params)
end
@@ -81,7 +83,7 @@ RSpec.shared_examples 'additional metrics query' do
context 'with one group where two metrics is found' do
before do
- allow(metric_group_class).to receive(:all).and_return([simple_metric_group])
+ allow(metric_group_class).to receive(:common_metrics).and_return([simple_metric_group])
end
context 'some queries return results' do
@@ -117,7 +119,7 @@ RSpec.shared_examples 'additional metrics query' do
let(:metrics) { [simple_metric(queries: [simple_query])] }
before do
- allow(metric_group_class).to receive(:all).and_return(
+ allow(metric_group_class).to receive(:common_metrics).and_return(
[
simple_metric_group(name: 'group_a', metrics: [simple_metric(queries: [simple_query])]),
simple_metric_group(name: 'group_b', metrics: [simple_metric(title: 'title_b', queries: [simple_query('b')])])
diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
index 7ce80c82439..ea7dbade171 100644
--- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
@@ -89,6 +89,19 @@ shared_examples 'handle uploads' do
end
end
+ context "when neither the uploader nor the model exists" do
+ before do
+ allow_any_instance_of(Upload).to receive(:build_uploader).and_return(nil)
+ allow(controller).to receive(:find_model).and_return(nil)
+ end
+
+ it "responds with status 404" do
+ show_upload
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
context "when the file doesn't exist" do
before do
allow_any_instance_of(FileUploader).to receive(:exists?).and_return(false)
diff --git a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
index 4e18804b937..9fc2fbef449 100644
--- a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
+++ b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb
@@ -17,12 +17,88 @@ shared_examples 'custom attributes endpoints' do |attributable_name|
end
end
- it 'filters by custom attributes' do
- get api("/#{attributable_name}", admin), custom_attributes: { foo: 'foo', bar: 'bar' }
+ context 'with an authorized user' do
+ it 'filters by custom attributes' do
+ get api("/#{attributable_name}", admin), custom_attributes: { foo: 'foo', bar: 'bar' }
- expect(response).to have_gitlab_http_status(200)
- expect(json_response.size).to be 1
- expect(json_response.first['id']).to eq attributable.id
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to be 1
+ expect(json_response.first['id']).to eq attributable.id
+ end
+ end
+ end
+
+ describe "GET /#{attributable_name} with custom attributes" do
+ before do
+ other_attributable
+ end
+
+ context 'with an unauthorized user' do
+ it 'does not include custom attributes' do
+ get api("/#{attributable_name}", user), with_custom_attributes: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to be 2
+ expect(json_response.first).not_to include 'custom_attributes'
+ end
+ end
+
+ context 'with an authorized user' do
+ it 'does not include custom attributes by default' do
+ get api("/#{attributable_name}", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to be 2
+ expect(json_response.first).not_to include 'custom_attributes'
+ expect(json_response.second).not_to include 'custom_attributes'
+ end
+
+ it 'includes custom attributes if requested' do
+ get api("/#{attributable_name}", admin), with_custom_attributes: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to be 2
+
+ attributable_response = json_response.find { |r| r['id'] == attributable.id }
+ other_attributable_response = json_response.find { |r| r['id'] == other_attributable.id }
+
+ expect(attributable_response['custom_attributes']).to contain_exactly(
+ { 'key' => 'foo', 'value' => 'foo' },
+ { 'key' => 'bar', 'value' => 'bar' }
+ )
+
+ expect(other_attributable_response['custom_attributes']).to eq []
+ end
+ end
+ end
+
+ describe "GET /#{attributable_name}/:id with custom attributes" do
+ context 'with an unauthorized user' do
+ it 'does not include custom attributes' do
+ get api("/#{attributable_name}/#{attributable.id}", user), with_custom_attributes: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).not_to include 'custom_attributes'
+ end
+ end
+
+ context 'with an authorized user' do
+ it 'does not include custom attributes by default' do
+ get api("/#{attributable_name}/#{attributable.id}", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).not_to include 'custom_attributes'
+ end
+
+ it 'includes custom attributes if requested' do
+ get api("/#{attributable_name}/#{attributable.id}", admin), with_custom_attributes: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['custom_attributes']).to contain_exactly(
+ { 'key' => 'foo', 'value' => 'foo' },
+ { 'key' => 'bar', 'value' => 'bar' }
+ )
+ end
end
end
@@ -33,14 +109,16 @@ shared_examples 'custom attributes endpoints' do |attributable_name|
it_behaves_like 'an unauthorized API user'
end
- it 'returns all custom attributes' do
- get api("/#{attributable_name}/#{attributable.id}/custom_attributes", admin)
+ context 'with an authorized user' do
+ it 'returns all custom attributes' do
+ get api("/#{attributable_name}/#{attributable.id}/custom_attributes", admin)
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to contain_exactly(
- { 'key' => 'foo', 'value' => 'foo' },
- { 'key' => 'bar', 'value' => 'bar' }
- )
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to contain_exactly(
+ { 'key' => 'foo', 'value' => 'foo' },
+ { 'key' => 'bar', 'value' => 'bar' }
+ )
+ end
end
end
@@ -51,11 +129,13 @@ shared_examples 'custom attributes endpoints' do |attributable_name|
it_behaves_like 'an unauthorized API user'
end
- it 'returns a single custom attribute' do
- get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin)
+ context 'with an authorized user' do
+ it'returns a single custom attribute' do
+ get api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin)
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to eq({ 'key' => 'foo', 'value' => 'foo' })
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq({ 'key' => 'foo', 'value' => 'foo' })
+ end
end
end
@@ -66,24 +146,26 @@ shared_examples 'custom attributes endpoints' do |attributable_name|
it_behaves_like 'an unauthorized API user'
end
- it 'creates a new custom attribute' do
- expect do
- put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin), value: 'new'
- end.to change { attributable.custom_attributes.count }.by(1)
+ context 'with an authorized user' do
+ it 'creates a new custom attribute' do
+ expect do
+ put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin), value: 'new'
+ end.to change { attributable.custom_attributes.count }.by(1)
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to eq({ 'key' => 'new', 'value' => 'new' })
- expect(attributable.custom_attributes.find_by(key: 'new').value).to eq 'new'
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq({ 'key' => 'new', 'value' => 'new' })
+ expect(attributable.custom_attributes.find_by(key: 'new').value).to eq 'new'
+ end
- it 'updates an existing custom attribute' do
- expect do
- put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin), value: 'new'
- end.not_to change { attributable.custom_attributes.count }
+ it 'updates an existing custom attribute' do
+ expect do
+ put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin), value: 'new'
+ end.not_to change { attributable.custom_attributes.count }
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to eq({ 'key' => 'foo', 'value' => 'new' })
- expect(custom_attribute1.reload.value).to eq 'new'
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to eq({ 'key' => 'foo', 'value' => 'new' })
+ expect(custom_attribute1.reload.value).to eq 'new'
+ end
end
end
@@ -94,13 +176,15 @@ shared_examples 'custom attributes endpoints' do |attributable_name|
it_behaves_like 'an unauthorized API user'
end
- it 'deletes an existing custom attribute' do
- expect do
- delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin)
- end.to change { attributable.custom_attributes.count }.by(-1)
+ context 'with an authorized user' do
+ it 'deletes an existing custom attribute' do
+ expect do
+ delete api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin)
+ end.to change { attributable.custom_attributes.count }.by(-1)
- expect(response).to have_gitlab_http_status(204)
- expect(attributable.custom_attributes.find_by(key: 'foo')).to be_nil
+ expect(response).to have_gitlab_http_status(204)
+ expect(attributable.custom_attributes.find_by(key: 'foo')).to be_nil
+ end
end
end
end
diff --git a/spec/support/snippet_visibility.rb b/spec/support/snippet_visibility.rb
new file mode 100644
index 00000000000..3a7c69b7877
--- /dev/null
+++ b/spec/support/snippet_visibility.rb
@@ -0,0 +1,322 @@
+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/test_env.rb b/spec/support/test_env.rb
index c275522159c..01321989f01 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -1,5 +1,5 @@
require 'rspec/mocks'
-require 'toml'
+require 'toml-rb'
module TestEnv
extend self
diff --git a/spec/support/track_untracked_uploads_helpers.rb b/spec/support/track_untracked_uploads_helpers.rb
deleted file mode 100644
index 5752078d2a0..00000000000
--- a/spec/support/track_untracked_uploads_helpers.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module TrackUntrackedUploadsHelpers
- def uploaded_file
- fixture_path = Rails.root.join('spec/fixtures/rails_sample.jpg')
- fixture_file_upload(fixture_path)
- end
-
- def ensure_temporary_tracking_table_exists
- Gitlab::BackgroundMigration::PrepareUntrackedUploads.new.send(:ensure_temporary_tracking_table_exists)
- end
-
- def drop_temp_table_if_exists
- ActiveRecord::Base.connection.drop_table(:untracked_files_for_uploads) if ActiveRecord::Base.connection.table_exists?(:untracked_files_for_uploads)
- end
-
- def create_or_update_appearance(attrs)
- a = Appearance.first_or_initialize(title: 'foo', description: 'bar')
- a.update!(attrs)
- a
- end
-end
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index b37d6ac831f..1f4053ff9ad 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -132,7 +132,7 @@ describe 'gitlab:gitaly namespace rake task' do
expect { run_rake_task('gitlab:gitaly:storage_config')}
.to output(expected_output).to_stdout
- parsed_output = TOML.parse(expected_output)
+ parsed_output = TomlRB.parse(expected_output)
config.each do |name, params|
expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params['path'] })
end
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index ce6b0620fdc..7373aad7796 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe FileUploader do
let(:group) { create(:group, name: 'awesome') }
- let(:project) { create(:project, namespace: group, name: 'project') }
+ let(:project) { create(:project, :legacy_storage, namespace: group, name: 'project') }
let(:uploader) { described_class.new(project) }
let(:upload) { double(model: project, path: 'secret/foo.jpg') }
@@ -16,12 +16,12 @@ describe FileUploader do
shared_examples 'uses hashed storage' do
context 'when rolled out attachments' do
+ let(:project) { build_stubbed(:project, namespace: group, name: 'project') }
+
before do
allow(project).to receive(:disk_path).and_return('ca/fe/fe/ed')
end
- let(:project) { build_stubbed(:project, :hashed, namespace: group, name: 'project') }
-
it_behaves_like 'builds correct paths',
store_dir: %r{ca/fe/fe/ed/\h+},
absolute_path: %r{#{described_class.root}/ca/fe/fe/ed/secret/foo.jpg}
diff --git a/spec/validators/variable_duplicates_validator_spec.rb b/spec/validators/variable_duplicates_validator_spec.rb
new file mode 100644
index 00000000000..0b71a67f94d
--- /dev/null
+++ b/spec/validators/variable_duplicates_validator_spec.rb
@@ -0,0 +1,67 @@
+require 'spec_helper'
+
+describe VariableDuplicatesValidator do
+ let(:validator) { described_class.new(attributes: [:variables], **options) }
+
+ describe '#validate_each' do
+ let(:project) { build(:project) }
+
+ subject { validator.validate_each(project, :variables, project.variables) }
+
+ context 'with no scope' do
+ let(:options) { {} }
+ let(:variables) { build_list(:ci_variable, 2, project: project) }
+
+ before do
+ project.variables << variables
+ end
+
+ it 'does not have any errors' do
+ subject
+
+ expect(project.errors.empty?).to be true
+ end
+
+ context 'with duplicates' do
+ before do
+ project.variables.build(key: variables.first.key, value: 'dummy_value')
+ end
+
+ it 'has a duplicate key error' do
+ subject
+
+ expect(project.errors).to have_key(:variables)
+ end
+ end
+ end
+
+ context 'with a scope attribute' do
+ let(:options) { { scope: :environment_scope } }
+ let(:first_variable) { build(:ci_variable, key: 'test_key', environment_scope: '*', project: project) }
+ let(:second_variable) { build(:ci_variable, key: 'test_key', environment_scope: 'prod', project: project) }
+
+ before do
+ project.variables << first_variable
+ project.variables << second_variable
+ end
+
+ it 'does not have any errors' do
+ subject
+
+ expect(project.errors.empty?).to be true
+ end
+
+ context 'with duplicates' do
+ before do
+ project.variables.build(key: second_variable.key, value: 'dummy_value', environment_scope: second_variable.environment_scope)
+ end
+
+ it 'has a duplicate key error' do
+ subject
+
+ expect(project.errors).to have_key(:variables)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/views/shared/projects/_project.html.haml_spec.rb b/spec/views/shared/projects/_project.html.haml_spec.rb
index f0a4f153699..3b14045e61f 100644
--- a/spec/views/shared/projects/_project.html.haml_spec.rb
+++ b/spec/views/shared/projects/_project.html.haml_spec.rb
@@ -5,6 +5,7 @@ describe 'shared/projects/_project.html.haml' do
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
+ allow(view).to receive(:can?) { true }
end
it 'should render creator avatar if project has a creator' do
diff --git a/spec/workers/authorized_projects_worker_spec.rb b/spec/workers/authorized_projects_worker_spec.rb
index 0d6eb536c33..d095138f6b7 100644
--- a/spec/workers/authorized_projects_worker_spec.rb
+++ b/spec/workers/authorized_projects_worker_spec.rb
@@ -1,79 +1,6 @@
require 'spec_helper'
describe AuthorizedProjectsWorker do
- let(:project) { create(:project) }
-
- def build_args_list(*ids, multiply: 1)
- args_list = ids.map { |id| [id] }
- args_list * multiply
- end
-
- describe '.bulk_perform_and_wait' do
- it 'schedules the ids and waits for the jobs to complete' do
- args_list = build_args_list(project.owner.id)
-
- project.owner.project_authorizations.delete_all
- described_class.bulk_perform_and_wait(args_list)
-
- expect(project.owner.project_authorizations.count).to eq(1)
- end
-
- it 'inlines workloads <= 3 jobs' do
- args_list = build_args_list(project.owner.id, multiply: 3)
- expect(described_class).to receive(:bulk_perform_inline).with(args_list)
-
- described_class.bulk_perform_and_wait(args_list)
- end
-
- it 'runs > 3 jobs using sidekiq' do
- project.owner.project_authorizations.delete_all
-
- expect(described_class).to receive(:bulk_perform_async).and_call_original
-
- args_list = build_args_list(project.owner.id, multiply: 4)
- described_class.bulk_perform_and_wait(args_list)
-
- expect(project.owner.project_authorizations.count).to eq(1)
- end
- end
-
- describe '.bulk_perform_inline' do
- it 'refreshes the authorizations inline' do
- project.owner.project_authorizations.delete_all
-
- expect_any_instance_of(described_class).to receive(:perform).and_call_original
-
- described_class.bulk_perform_inline(build_args_list(project.owner.id))
-
- expect(project.owner.project_authorizations.count).to eq(1)
- end
-
- it 'enqueues jobs if an error is raised' do
- invalid_id = -1
- args_list = build_args_list(project.owner.id, invalid_id)
-
- allow_any_instance_of(described_class).to receive(:perform).with(project.owner.id)
- allow_any_instance_of(described_class).to receive(:perform).with(invalid_id).and_raise(ArgumentError)
- expect(described_class).to receive(:bulk_perform_async).with(build_args_list(invalid_id))
-
- described_class.bulk_perform_inline(args_list)
- end
- end
-
- describe '.bulk_perform_async' do
- it "uses it's respective sidekiq queue" do
- args_list = build_args_list(project.owner.id)
- push_bulk_args = {
- 'class' => described_class,
- 'args' => args_list
- }
-
- expect(Sidekiq::Client).to receive(:push_bulk).with(push_bulk_args).once
-
- described_class.bulk_perform_async(args_list)
- end
- end
-
describe '#perform' do
let(:user) { create(:user) }
@@ -85,12 +12,6 @@ describe AuthorizedProjectsWorker do
job.perform(user.id)
end
- it 'notifies the JobWaiter when done if the key is provided' do
- expect(Gitlab::JobWaiter).to receive(:notify).with('notify-key', job.jid)
-
- job.perform(user.id, 'notify-key')
- end
-
context "when the user is not found" do
it "does nothing" do
expect_any_instance_of(User).not_to receive(:refresh_authorized_projects)
diff --git a/spec/workers/check_gcp_project_billing_worker_spec.rb b/spec/workers/check_gcp_project_billing_worker_spec.rb
index 7b7a7c1bc44..526ecf75921 100644
--- a/spec/workers/check_gcp_project_billing_worker_spec.rb
+++ b/spec/workers/check_gcp_project_billing_worker_spec.rb
@@ -6,6 +6,11 @@ describe CheckGcpProjectBillingWorker do
subject { described_class.new.perform('token_key') }
+ before do
+ allow(described_class).to receive(:get_billing_state)
+ allow_any_instance_of(described_class).to receive(:update_billing_change_counter)
+ end
+
context 'when there is a token in redis' do
before do
allow(described_class).to receive(:get_session_token).and_return(token)
@@ -23,11 +28,8 @@ describe CheckGcpProjectBillingWorker do
end
it 'stores billing status in redis' do
- redis_double = double
-
expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
- expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
- expect(redis_double).to receive(:set).with(described_class.redis_shared_state_key_for(token), anything, anything)
+ expect(described_class).to receive(:set_billing_state).with(token, true)
subject
end
@@ -48,7 +50,7 @@ describe CheckGcpProjectBillingWorker do
context 'when there is no token in redis' do
before do
- allow_any_instance_of(described_class).to receive(:get_session_token).and_return(nil)
+ allow(described_class).to receive(:get_session_token).and_return(nil)
end
it 'does not call the service' do
@@ -58,4 +60,57 @@ describe CheckGcpProjectBillingWorker do
end
end
end
+
+ describe 'billing change counter' do
+ subject { described_class.new.perform('token_key') }
+
+ before do
+ allow(described_class).to receive(:get_session_token).and_return('bogustoken')
+ allow_any_instance_of(described_class).to receive(:try_obtain_lease_for).and_return('randomuuid')
+ allow(described_class).to receive(:set_billing_state)
+ end
+
+ context 'when previous state was false' do
+ before do
+ expect(described_class).to receive(:get_billing_state).and_return(false)
+ end
+
+ context 'when the current state is false' do
+ before do
+ expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([])
+ end
+
+ it 'increments the billing change counter' do
+ expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
+
+ subject
+ end
+ end
+
+ context 'when the current state is true' do
+ before do
+ expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
+ end
+
+ it 'increments the billing change counter' do
+ expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
+
+ subject
+ end
+ end
+ end
+
+ context 'when previous state was true' do
+ before do
+ expect(described_class).to receive(:get_billing_state).and_return(true)
+ expect(CheckGcpProjectBillingService).to receive_message_chain(:new, :execute).and_return([double])
+ end
+
+ it 'increment the billing change counter' do
+ expect_any_instance_of(described_class).to receive_message_chain(:billing_changed_counter, :increment)
+
+ subject
+ end
+ end
+ end
end
diff --git a/spec/workers/concerns/waitable_worker_spec.rb b/spec/workers/concerns/waitable_worker_spec.rb
new file mode 100644
index 00000000000..4af0de86ac9
--- /dev/null
+++ b/spec/workers/concerns/waitable_worker_spec.rb
@@ -0,0 +1,92 @@
+require 'spec_helper'
+
+describe WaitableWorker do
+ let(:worker) do
+ Class.new do
+ def self.name
+ 'Gitlab::Foo::Bar::DummyWorker'
+ end
+
+ class << self
+ cattr_accessor(:counter) { 0 }
+ end
+
+ include ApplicationWorker
+ prepend WaitableWorker
+
+ def perform(i = 0)
+ self.class.counter += i
+ end
+ end
+ end
+
+ subject(:job) { worker.new }
+
+ describe '.bulk_perform_and_wait' do
+ it 'schedules the jobs and waits for them to complete' do
+ worker.bulk_perform_and_wait([[1], [2]])
+
+ expect(worker.counter).to eq(3)
+ end
+
+ it 'inlines workloads <= 3 jobs' do
+ args_list = [[1], [2], [3]]
+ expect(worker).to receive(:bulk_perform_inline).with(args_list).and_call_original
+
+ worker.bulk_perform_and_wait(args_list)
+
+ expect(worker.counter).to eq(6)
+ end
+
+ it 'runs > 3 jobs using sidekiq' do
+ expect(worker).to receive(:bulk_perform_async)
+
+ worker.bulk_perform_and_wait([[1], [2], [3], [4]])
+ end
+ end
+
+ describe '.bulk_perform_inline' do
+ it 'runs the jobs inline' do
+ expect(worker).not_to receive(:bulk_perform_async)
+
+ worker.bulk_perform_inline([[1], [2]])
+
+ expect(worker.counter).to eq(3)
+ end
+
+ it 'enqueues jobs if an error is raised' do
+ expect(worker).to receive(:bulk_perform_async).with([['foo']])
+
+ worker.bulk_perform_inline([[1], ['foo']])
+ end
+ end
+
+ describe '#perform' do
+ shared_examples 'perform' do
+ it 'notifies the JobWaiter when done if the key is provided' do
+ key = Gitlab::JobWaiter.new.key
+ expect(Gitlab::JobWaiter).to receive(:notify).with(key, job.jid)
+
+ job.perform(*args, key)
+ end
+
+ it 'does not notify the JobWaiter when done if no key is provided' do
+ expect(Gitlab::JobWaiter).not_to receive(:notify)
+
+ job.perform(*args)
+ end
+ end
+
+ context 'when the worker takes arguments' do
+ let(:args) { [1] }
+
+ it_behaves_like 'perform'
+ end
+
+ context 'when the worker takes no arguments' do
+ let(:args) { [] }
+
+ it_behaves_like 'perform'
+ end
+ end
+end
diff --git a/spec/workers/pages_domain_verification_cron_worker_spec.rb b/spec/workers/pages_domain_verification_cron_worker_spec.rb
new file mode 100644
index 00000000000..8f780428c82
--- /dev/null
+++ b/spec/workers/pages_domain_verification_cron_worker_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe PagesDomainVerificationCronWorker do
+ subject(:worker) { described_class.new }
+
+ describe '#perform' do
+ it 'enqueues a PagesDomainVerificationWorker for domains needing verification' do
+ verified = create(:pages_domain)
+ reverify = create(:pages_domain, :reverify)
+ disabled = create(:pages_domain, :disabled)
+
+ [reverify, disabled].each do |domain|
+ expect(PagesDomainVerificationWorker).to receive(:perform_async).with(domain.id)
+ end
+
+ expect(PagesDomainVerificationWorker).not_to receive(:perform_async).with(verified.id)
+
+ worker.perform
+ end
+ end
+end
diff --git a/spec/workers/pages_domain_verification_worker_spec.rb b/spec/workers/pages_domain_verification_worker_spec.rb
new file mode 100644
index 00000000000..372fc95ab4a
--- /dev/null
+++ b/spec/workers/pages_domain_verification_worker_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe PagesDomainVerificationWorker do
+ subject(:worker) { described_class.new }
+
+ let(:domain) { create(:pages_domain) }
+
+ describe '#perform' do
+ it 'does nothing for a non-existent domain' do
+ domain.destroy
+
+ expect(VerifyPagesDomainService).not_to receive(:new)
+
+ expect { worker.perform(domain.id) }.not_to raise_error
+ end
+
+ it 'delegates to VerifyPagesDomainService' do
+ service = double(:service)
+ expected_domain = satisfy { |obj| obj == domain }
+
+ expect(VerifyPagesDomainService).to receive(:new).with(expected_domain) { service }
+ expect(service).to receive(:execute)
+
+ worker.perform(domain.id)
+ end
+ end
+end
diff --git a/spec/workers/process_commit_worker_spec.rb b/spec/workers/process_commit_worker_spec.rb
index 24f8ca67594..76ef57b6b1e 100644
--- a/spec/workers/process_commit_worker_spec.rb
+++ b/spec/workers/process_commit_worker_spec.rb
@@ -20,6 +20,32 @@ describe ProcessCommitWorker do
worker.perform(project.id, -1, commit.to_hash)
end
+ context 'when commit is a merge request merge commit' do
+ let(:merge_request) do
+ create(:merge_request,
+ description: "Closes #{issue.to_reference}",
+ source_branch: 'feature-merged',
+ target_branch: 'master',
+ source_project: project)
+ end
+
+ let(:commit) do
+ project.repository.create_branch('feature-merged', 'feature')
+
+ sha = project.repository.merge(user,
+ merge_request.diff_head_sha,
+ merge_request,
+ "Closes #{issue.to_reference}")
+ project.repository.commit(sha)
+ end
+
+ it 'it does not close any issues from the commit message' do
+ expect(worker).not_to receive(:close_issues)
+
+ worker.perform(project.id, user.id, commit.to_hash)
+ end
+ end
+
it 'processes the commit message' do
expect(worker).to receive(:process_commit_message).and_call_original
@@ -48,11 +74,9 @@ describe ProcessCommitWorker do
describe '#process_commit_message' do
context 'when pushing to the default branch' do
it 'closes issues that should be closed per the commit message' do
- allow(commit).to receive(:safe_message)
- .and_return("Closes #{issue.to_reference}")
+ allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
- expect(worker).to receive(:close_issues)
- .with(project, user, user, commit, [issue])
+ expect(worker).to receive(:close_issues).with(project, user, user, commit, [issue])
worker.process_commit_message(project, commit, user, user, true)
end
@@ -60,8 +84,7 @@ describe ProcessCommitWorker do
context 'when pushing to a non-default branch' do
it 'does not close any issues' do
- allow(commit).to receive(:safe_message)
- .and_return("Closes #{issue.to_reference}")
+ allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
expect(worker).not_to receive(:close_issues)
@@ -102,8 +125,7 @@ describe ProcessCommitWorker do
describe '#update_issue_metrics' do
it 'updates any existing issue metrics' do
- allow(commit).to receive(:safe_message)
- .and_return("Closes #{issue.to_reference}")
+ allow(commit).to receive(:safe_message).and_return("Closes #{issue.to_reference}")
worker.update_issue_metrics(commit, user)
@@ -113,10 +135,10 @@ describe ProcessCommitWorker do
end
it "doesn't execute any queries with false conditions" do
- allow(commit).to receive(:safe_message)
- .and_return("Lorem Ipsum")
+ allow(commit).to receive(:safe_message).and_return("Lorem Ipsum")
- expect { worker.update_issue_metrics(commit, user) }.not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
+ expect { worker.update_issue_metrics(commit, user) }
+ .not_to make_queries_matching(/WHERE (?:1=0|0=1)/)
end
end
@@ -128,8 +150,9 @@ describe ProcessCommitWorker do
end
it 'parses date strings into Time instances' do
- commit = worker
- .build_commit(project, id: '123', authored_date: Time.now.to_s)
+ commit = worker.build_commit(project,
+ id: '123',
+ authored_date: Time.now.to_s)
expect(commit.authored_date).to be_an_instance_of(Time)
end
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 4912baa348c..6c66658d8c3 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -68,7 +68,7 @@ describe RepositoryForkWorker do
end
it "handles bad fork" do
- error_message = "Unable to fork project #{fork_project.id} for repository #{project.full_path} -> #{fork_project.full_path}"
+ error_message = "Unable to fork project #{fork_project.id} for repository #{project.disk_path} -> #{fork_project.disk_path}"
expect_fork_repository.and_return(false)
diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb
index 8619ff2f7da..ff625164142 100644
--- a/spec/workers/storage_migrator_worker_spec.rb
+++ b/spec/workers/storage_migrator_worker_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe StorageMigratorWorker do
subject(:worker) { described_class.new }
- let(:projects) { create_list(:project, 2) }
+ let(:projects) { create_list(:project, 2, :legacy_storage) }
describe '#perform' do
let(:ids) { projects.map(&:id) }
diff --git a/spec/workers/stuck_import_jobs_worker_spec.rb b/spec/workers/stuck_import_jobs_worker_spec.rb
index a82eb54ffe4..069514552b1 100644
--- a/spec/workers/stuck_import_jobs_worker_spec.rb
+++ b/spec/workers/stuck_import_jobs_worker_spec.rb
@@ -2,35 +2,59 @@ require 'spec_helper'
describe StuckImportJobsWorker do
let(:worker) { described_class.new }
- let(:exclusive_lease_uuid) { SecureRandom.uuid }
- before do
- allow_any_instance_of(Gitlab::ExclusiveLease).to receive(:try_obtain).and_return(exclusive_lease_uuid)
- end
+ shared_examples 'project import job detection' do
+ context 'when the job has completed' do
+ context 'when the import status was already updated' do
+ before do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids) do
+ project.import_start
+ project.import_finish
- describe 'with started import_status' do
- let(:project) { create(:project, :import_started, import_jid: '123') }
+ [project.import_jid]
+ end
+ end
+
+ it 'does not mark the project as failed' do
+ worker.perform
+
+ expect(project.reload.import_status).to eq('finished')
+ end
+ end
+
+ context 'when the import status was not updated' do
+ before do
+ allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([project.import_jid])
+ end
- describe 'long running import' do
- it 'marks the project as failed' do
- allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return(['123'])
+ it 'marks the project as failed' do
+ worker.perform
- expect { worker.perform }.to change { project.reload.import_status }.to('failed')
+ expect(project.reload.import_status).to eq('failed')
+ end
end
end
- describe 'running import' do
- it 'does not mark the project as failed' do
+ context 'when the job is still in Sidekiq' do
+ before do
allow(Gitlab::SidekiqStatus).to receive(:completed_jids).and_return([])
+ end
+ it 'does not mark the project as failed' do
expect { worker.perform }.not_to change { project.reload.import_status }
end
+ end
+ end
- describe 'import without import_jid' do
- it 'marks the project as failed' do
- expect { worker.perform }.to change { project.reload.import_status }.to('failed')
- end
- end
+ describe 'with scheduled import_status' do
+ it_behaves_like 'project import job detection' do
+ let(:project) { create(:project, :import_scheduled, import_jid: '123') }
+ end
+ end
+
+ describe 'with started import_status' do
+ it_behaves_like 'project import job detection' do
+ let(:project) { create(:project, :import_started, import_jid: '123') }
end
end
end
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.eot b/vendor/assets/fonts/KaTeX_AMS-Regular.eot
deleted file mode 100644
index 784276a3cbf..00000000000
--- a/vendor/assets/fonts/KaTeX_AMS-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.ttf b/vendor/assets/fonts/KaTeX_AMS-Regular.ttf
deleted file mode 100644
index 6f1e0be2028..00000000000
--- a/vendor/assets/fonts/KaTeX_AMS-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.woff b/vendor/assets/fonts/KaTeX_AMS-Regular.woff
deleted file mode 100644
index 4dded4733b3..00000000000
--- a/vendor/assets/fonts/KaTeX_AMS-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_AMS-Regular.woff2 b/vendor/assets/fonts/KaTeX_AMS-Regular.woff2
deleted file mode 100644
index ea81079c4e2..00000000000
--- a/vendor/assets/fonts/KaTeX_AMS-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.eot b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.eot
deleted file mode 100644
index 1a0db0c568e..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttf b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttf
deleted file mode 100644
index b94907dad11..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff
deleted file mode 100644
index 799fa8122ca..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2 b/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2
deleted file mode 100644
index 73bb5422878..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Bold.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.eot b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.eot
deleted file mode 100644
index 6cc83d0922c..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttf b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttf
deleted file mode 100644
index cf51e2021e4..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff
deleted file mode 100644
index f5e5c623577..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2 b/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2
deleted file mode 100644
index dd76d3488d5..00000000000
--- a/vendor/assets/fonts/KaTeX_Caligraphic-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.eot b/vendor/assets/fonts/KaTeX_Fraktur-Bold.eot
deleted file mode 100644
index 1960b106656..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Bold.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.ttf b/vendor/assets/fonts/KaTeX_Fraktur-Bold.ttf
deleted file mode 100644
index 7b0790f1ae8..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Bold.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff b/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff
deleted file mode 100644
index dc325713291..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2 b/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2
deleted file mode 100644
index fdc429227ad..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Bold.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.eot b/vendor/assets/fonts/KaTeX_Fraktur-Regular.eot
deleted file mode 100644
index e4e73796aea..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.ttf b/vendor/assets/fonts/KaTeX_Fraktur-Regular.ttf
deleted file mode 100644
index 063bc0263eb..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff b/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff
deleted file mode 100644
index c4b18d863f3..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2 b/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2
deleted file mode 100644
index 4318d938e26..00000000000
--- a/vendor/assets/fonts/KaTeX_Fraktur-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.eot b/vendor/assets/fonts/KaTeX_Main-Bold.eot
deleted file mode 100644
index 80fbd022363..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Bold.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.ttf b/vendor/assets/fonts/KaTeX_Main-Bold.ttf
deleted file mode 100644
index 8e10722afae..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Bold.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.woff b/vendor/assets/fonts/KaTeX_Main-Bold.woff
deleted file mode 100644
index 43b361a6005..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Bold.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Bold.woff2 b/vendor/assets/fonts/KaTeX_Main-Bold.woff2
deleted file mode 100644
index af57a96c148..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Bold.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.eot b/vendor/assets/fonts/KaTeX_Main-Italic.eot
deleted file mode 100644
index fc770166b5e..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Italic.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.ttf b/vendor/assets/fonts/KaTeX_Main-Italic.ttf
deleted file mode 100644
index d124495d7b6..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Italic.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.woff b/vendor/assets/fonts/KaTeX_Main-Italic.woff
deleted file mode 100644
index e623236bc44..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Italic.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Italic.woff2 b/vendor/assets/fonts/KaTeX_Main-Italic.woff2
deleted file mode 100644
index 944e9740bdf..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Italic.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.eot b/vendor/assets/fonts/KaTeX_Main-Regular.eot
deleted file mode 100644
index dc60c090c7a..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.ttf b/vendor/assets/fonts/KaTeX_Main-Regular.ttf
deleted file mode 100644
index da5797ffcce..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.woff b/vendor/assets/fonts/KaTeX_Main-Regular.woff
deleted file mode 100644
index 37db672e821..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Main-Regular.woff2 b/vendor/assets/fonts/KaTeX_Main-Regular.woff2
deleted file mode 100644
index 48820424893..00000000000
--- a/vendor/assets/fonts/KaTeX_Main-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.eot b/vendor/assets/fonts/KaTeX_Math-BoldItalic.eot
deleted file mode 100644
index 52c8b8c6b40..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-BoldItalic.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.ttf b/vendor/assets/fonts/KaTeX_Math-BoldItalic.ttf
deleted file mode 100644
index a8b527c7ef6..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-BoldItalic.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff b/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff
deleted file mode 100644
index 8940e0b5801..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2 b/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2
deleted file mode 100644
index 15cf56d3408..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-BoldItalic.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.eot b/vendor/assets/fonts/KaTeX_Math-Italic.eot
deleted file mode 100644
index 64c8992c477..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Italic.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.ttf b/vendor/assets/fonts/KaTeX_Math-Italic.ttf
deleted file mode 100644
index 06f39d3a299..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Italic.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.woff b/vendor/assets/fonts/KaTeX_Math-Italic.woff
deleted file mode 100644
index cf3b4b79e5b..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Italic.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Italic.woff2 b/vendor/assets/fonts/KaTeX_Math-Italic.woff2
deleted file mode 100644
index 5f8c4bfa455..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Italic.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.eot b/vendor/assets/fonts/KaTeX_Math-Regular.eot
deleted file mode 100644
index 5521e6a564d..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.ttf b/vendor/assets/fonts/KaTeX_Math-Regular.ttf
deleted file mode 100644
index 73127082370..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.woff b/vendor/assets/fonts/KaTeX_Math-Regular.woff
deleted file mode 100644
index 0e2ebdf18af..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Math-Regular.woff2 b/vendor/assets/fonts/KaTeX_Math-Regular.woff2
deleted file mode 100644
index ebe3d028a34..00000000000
--- a/vendor/assets/fonts/KaTeX_Math-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.eot b/vendor/assets/fonts/KaTeX_SansSerif-Bold.eot
deleted file mode 100644
index 1660e76a2b6..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Bold.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.ttf b/vendor/assets/fonts/KaTeX_SansSerif-Bold.ttf
deleted file mode 100644
index dbeb7b92ab5..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Bold.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff b/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff
deleted file mode 100644
index 8f144a8bb31..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2 b/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2
deleted file mode 100644
index 329e85557fa..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Bold.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.eot b/vendor/assets/fonts/KaTeX_SansSerif-Italic.eot
deleted file mode 100644
index 289ae3ff8b7..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Italic.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.ttf b/vendor/assets/fonts/KaTeX_SansSerif-Italic.ttf
deleted file mode 100644
index b3a2f38f224..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Italic.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff b/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff
deleted file mode 100644
index bddf7ea6579..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2 b/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2
deleted file mode 100644
index 5fa767bddd6..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Italic.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.eot b/vendor/assets/fonts/KaTeX_SansSerif-Regular.eot
deleted file mode 100644
index 1b38b98a180..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.ttf b/vendor/assets/fonts/KaTeX_SansSerif-Regular.ttf
deleted file mode 100644
index e4712f84775..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff b/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff
deleted file mode 100644
index 33be368048f..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2 b/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2
deleted file mode 100644
index 4fcb2e29a05..00000000000
--- a/vendor/assets/fonts/KaTeX_SansSerif-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.eot b/vendor/assets/fonts/KaTeX_Script-Regular.eot
deleted file mode 100644
index 7870d7f319b..00000000000
--- a/vendor/assets/fonts/KaTeX_Script-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.ttf b/vendor/assets/fonts/KaTeX_Script-Regular.ttf
deleted file mode 100644
index da4d11308ae..00000000000
--- a/vendor/assets/fonts/KaTeX_Script-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.woff b/vendor/assets/fonts/KaTeX_Script-Regular.woff
deleted file mode 100644
index d6ae79f998a..00000000000
--- a/vendor/assets/fonts/KaTeX_Script-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Script-Regular.woff2 b/vendor/assets/fonts/KaTeX_Script-Regular.woff2
deleted file mode 100644
index 1b43deb45a8..00000000000
--- a/vendor/assets/fonts/KaTeX_Script-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.eot b/vendor/assets/fonts/KaTeX_Size1-Regular.eot
deleted file mode 100644
index 29950f95ff6..00000000000
--- a/vendor/assets/fonts/KaTeX_Size1-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.ttf b/vendor/assets/fonts/KaTeX_Size1-Regular.ttf
deleted file mode 100644
index 194466a655d..00000000000
--- a/vendor/assets/fonts/KaTeX_Size1-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.woff b/vendor/assets/fonts/KaTeX_Size1-Regular.woff
deleted file mode 100644
index 237f271edd1..00000000000
--- a/vendor/assets/fonts/KaTeX_Size1-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size1-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size1-Regular.woff2
deleted file mode 100644
index 39b6f8f746c..00000000000
--- a/vendor/assets/fonts/KaTeX_Size1-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.eot b/vendor/assets/fonts/KaTeX_Size2-Regular.eot
deleted file mode 100644
index b8b0536f967..00000000000
--- a/vendor/assets/fonts/KaTeX_Size2-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.ttf b/vendor/assets/fonts/KaTeX_Size2-Regular.ttf
deleted file mode 100644
index b41b66a638f..00000000000
--- a/vendor/assets/fonts/KaTeX_Size2-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.woff b/vendor/assets/fonts/KaTeX_Size2-Regular.woff
deleted file mode 100644
index 4a3055854ed..00000000000
--- a/vendor/assets/fonts/KaTeX_Size2-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size2-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size2-Regular.woff2
deleted file mode 100644
index 3facec1ab89..00000000000
--- a/vendor/assets/fonts/KaTeX_Size2-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.eot b/vendor/assets/fonts/KaTeX_Size3-Regular.eot
deleted file mode 100644
index 576b864fae6..00000000000
--- a/vendor/assets/fonts/KaTeX_Size3-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.ttf b/vendor/assets/fonts/KaTeX_Size3-Regular.ttf
deleted file mode 100644
index 790ddbbc55f..00000000000
--- a/vendor/assets/fonts/KaTeX_Size3-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.woff b/vendor/assets/fonts/KaTeX_Size3-Regular.woff
deleted file mode 100644
index 3a6d062e660..00000000000
--- a/vendor/assets/fonts/KaTeX_Size3-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size3-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size3-Regular.woff2
deleted file mode 100644
index 2cffafe5018..00000000000
--- a/vendor/assets/fonts/KaTeX_Size3-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.eot b/vendor/assets/fonts/KaTeX_Size4-Regular.eot
deleted file mode 100644
index c2b045fc3db..00000000000
--- a/vendor/assets/fonts/KaTeX_Size4-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.ttf b/vendor/assets/fonts/KaTeX_Size4-Regular.ttf
deleted file mode 100644
index ce660aa7ff9..00000000000
--- a/vendor/assets/fonts/KaTeX_Size4-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.woff b/vendor/assets/fonts/KaTeX_Size4-Regular.woff
deleted file mode 100644
index 7826c6c97a1..00000000000
--- a/vendor/assets/fonts/KaTeX_Size4-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Size4-Regular.woff2 b/vendor/assets/fonts/KaTeX_Size4-Regular.woff2
deleted file mode 100644
index c92189812d9..00000000000
--- a/vendor/assets/fonts/KaTeX_Size4-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.eot b/vendor/assets/fonts/KaTeX_Typewriter-Regular.eot
deleted file mode 100644
index 4c178f484a8..00000000000
--- a/vendor/assets/fonts/KaTeX_Typewriter-Regular.eot
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.ttf b/vendor/assets/fonts/KaTeX_Typewriter-Regular.ttf
deleted file mode 100644
index b0427ad0a56..00000000000
--- a/vendor/assets/fonts/KaTeX_Typewriter-Regular.ttf
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff b/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff
deleted file mode 100644
index 78e990488a9..00000000000
--- a/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2 b/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2
deleted file mode 100644
index 618de99d480..00000000000
--- a/vendor/assets/fonts/KaTeX_Typewriter-Regular.woff2
+++ /dev/null
Binary files differ
diff --git a/vendor/assets/javascripts/Chart.js b/vendor/assets/javascripts/Chart.js
deleted file mode 100644
index c264262ba73..00000000000
--- a/vendor/assets/javascripts/Chart.js
+++ /dev/null
@@ -1,3477 +0,0 @@
-/*!
- * Chart.js
- * http://chartjs.org/
- * Version: 1.0.2
- *
- * Copyright 2015 Nick Downie
- * Released under the MIT license
- * https://github.com/nnnick/Chart.js/blob/master/LICENSE.md
- */
-
-
-(function(){
-
- "use strict";
-
- //Declare root variable - window in the browser, global on the server
- var root = this,
- previous = root.Chart;
-
- //Occupy the global variable of Chart, and create a simple base class
- var Chart = function(context){
- var chart = this;
- this.canvas = context.canvas;
-
- this.ctx = context;
-
- //Variables global to the chart
- var computeDimension = function(element,dimension)
- {
- if (element['offset'+dimension])
- {
- return element['offset'+dimension];
- }
- else
- {
- return document.defaultView.getComputedStyle(element).getPropertyValue(dimension);
- }
- }
-
- var width = this.width = computeDimension(context.canvas,'Width');
- var height = this.height = computeDimension(context.canvas,'Height');
-
- // Firefox requires this to work correctly
- context.canvas.width = width;
- context.canvas.height = height;
-
- var width = this.width = context.canvas.width;
- var height = this.height = context.canvas.height;
- this.aspectRatio = this.width / this.height;
- //High pixel density displays - multiply the size of the canvas height/width by the device pixel ratio, then scale.
- helpers.retinaScale(this);
-
- return this;
- };
- //Globally expose the defaults to allow for user updating/changing
- Chart.defaults = {
- global: {
- // Boolean - Whether to animate the chart
- animation: true,
-
- // Number - Number of animation steps
- animationSteps: 60,
-
- // String - Animation easing effect
- animationEasing: "easeOutQuart",
-
- // Boolean - If we should show the scale at all
- showScale: true,
-
- // Boolean - If we want to override with a hard coded scale
- scaleOverride: false,
-
- // ** Required if scaleOverride is true **
- // Number - The number of steps in a hard coded scale
- scaleSteps: null,
- // Number - The value jump in the hard coded scale
- scaleStepWidth: null,
- // Number - The scale starting value
- scaleStartValue: null,
-
- // String - Colour of the scale line
- scaleLineColor: "rgba(0,0,0,.1)",
-
- // Number - Pixel width of the scale line
- scaleLineWidth: 1,
-
- // Boolean - Whether to show labels on the scale
- scaleShowLabels: true,
-
- // Interpolated JS string - can access value
- scaleLabel: "<%=value%>",
-
- // Boolean - Whether the scale should stick to integers, and not show any floats even if drawing space is there
- scaleIntegersOnly: true,
-
- // Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
- scaleBeginAtZero: false,
-
- // String - Scale label font declaration for the scale label
- scaleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
-
- // Number - Scale label font size in pixels
- scaleFontSize: 12,
-
- // String - Scale label font weight style
- scaleFontStyle: "normal",
-
- // String - Scale label font colour
- scaleFontColor: "#666",
-
- // Boolean - whether or not the chart should be responsive and resize when the browser does.
- responsive: false,
-
- // Boolean - whether to maintain the starting aspect ratio or not when responsive, if set to false, will take up entire container
- maintainAspectRatio: true,
-
- // Boolean - Determines whether to draw tooltips on the canvas or not - attaches events to touchmove & mousemove
- showTooltips: true,
-
- // Boolean - Determines whether to draw built-in tooltip or call custom tooltip function
- customTooltips: false,
-
- // Array - Array of string names to attach tooltip events
- tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
-
- // String - Tooltip background colour
- tooltipFillColor: "rgba(0,0,0,0.8)",
-
- // String - Tooltip label font declaration for the scale label
- tooltipFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
-
- // Number - Tooltip label font size in pixels
- tooltipFontSize: 14,
-
- // String - Tooltip font weight style
- tooltipFontStyle: "normal",
-
- // String - Tooltip label font colour
- tooltipFontColor: "#fff",
-
- // String - Tooltip title font declaration for the scale label
- tooltipTitleFontFamily: "'Helvetica Neue', 'Helvetica', 'Arial', sans-serif",
-
- // Number - Tooltip title font size in pixels
- tooltipTitleFontSize: 14,
-
- // String - Tooltip title font weight style
- tooltipTitleFontStyle: "bold",
-
- // String - Tooltip title font colour
- tooltipTitleFontColor: "#fff",
-
- // Number - pixel width of padding around tooltip text
- tooltipYPadding: 6,
-
- // Number - pixel width of padding around tooltip text
- tooltipXPadding: 6,
-
- // Number - Size of the caret on the tooltip
- tooltipCaretSize: 8,
-
- // Number - Pixel radius of the tooltip border
- tooltipCornerRadius: 6,
-
- // Number - Pixel offset from point x to tooltip edge
- tooltipXOffset: 10,
-
- // String - Template string for single tooltips
- tooltipTemplate: "<%if (label){%><%=label%>: <%}%><%= value %>",
-
- // String - Template string for single tooltips
- multiTooltipTemplate: "<%= value %>",
-
- // String - Colour behind the legend colour block
- multiTooltipKeyBackground: '#fff',
-
- // Function - Will fire on animation progression.
- onAnimationProgress: function(){},
-
- // Function - Will fire on animation completion.
- onAnimationComplete: function(){}
-
- }
- };
-
- //Create a dictionary of chart types, to allow for extension of existing types
- Chart.types = {};
-
- //Global Chart helpers object for utility methods and classes
- var helpers = Chart.helpers = {};
-
- //-- Basic js utility methods
- var each = helpers.each = function(loopable,callback,self){
- var additionalArgs = Array.prototype.slice.call(arguments, 3);
- // Check to see if null or undefined firstly.
- if (loopable){
- if (loopable.length === +loopable.length){
- var i;
- for (i=0; i<loopable.length; i++){
- callback.apply(self,[loopable[i], i].concat(additionalArgs));
- }
- }
- else{
- for (var item in loopable){
- callback.apply(self,[loopable[item],item].concat(additionalArgs));
- }
- }
- }
- },
- clone = helpers.clone = function(obj){
- var objClone = {};
- each(obj,function(value,key){
- if (obj.hasOwnProperty(key)) objClone[key] = value;
- });
- return objClone;
- },
- extend = helpers.extend = function(base){
- each(Array.prototype.slice.call(arguments,1), function(extensionObject) {
- each(extensionObject,function(value,key){
- if (extensionObject.hasOwnProperty(key)) base[key] = value;
- });
- });
- return base;
- },
- merge = helpers.merge = function(base,master){
- //Merge properties in left object over to a shallow clone of object right.
- var args = Array.prototype.slice.call(arguments,0);
- args.unshift({});
- return extend.apply(null, args);
- },
- indexOf = helpers.indexOf = function(arrayToSearch, item){
- if (Array.prototype.indexOf) {
- return arrayToSearch.indexOf(item);
- }
- else{
- for (var i = 0; i < arrayToSearch.length; i++) {
- if (arrayToSearch[i] === item) return i;
- }
- return -1;
- }
- },
- where = helpers.where = function(collection, filterCallback){
- var filtered = [];
-
- helpers.each(collection, function(item){
- if (filterCallback(item)){
- filtered.push(item);
- }
- });
-
- return filtered;
- },
- findNextWhere = helpers.findNextWhere = function(arrayToSearch, filterCallback, startIndex){
- // Default to start of the array
- if (!startIndex){
- startIndex = -1;
- }
- for (var i = startIndex + 1; i < arrayToSearch.length; i++) {
- var currentItem = arrayToSearch[i];
- if (filterCallback(currentItem)){
- return currentItem;
- }
- }
- },
- findPreviousWhere = helpers.findPreviousWhere = function(arrayToSearch, filterCallback, startIndex){
- // Default to end of the array
- if (!startIndex){
- startIndex = arrayToSearch.length;
- }
- for (var i = startIndex - 1; i >= 0; i--) {
- var currentItem = arrayToSearch[i];
- if (filterCallback(currentItem)){
- return currentItem;
- }
- }
- },
- inherits = helpers.inherits = function(extensions){
- //Basic javascript inheritance based on the model created in Backbone.js
- var parent = this;
- var ChartElement = (extensions && extensions.hasOwnProperty("constructor")) ? extensions.constructor : function(){ return parent.apply(this, arguments); };
-
- var Surrogate = function(){ this.constructor = ChartElement;};
- Surrogate.prototype = parent.prototype;
- ChartElement.prototype = new Surrogate();
-
- ChartElement.extend = inherits;
-
- if (extensions) extend(ChartElement.prototype, extensions);
-
- ChartElement.__super__ = parent.prototype;
-
- return ChartElement;
- },
- noop = helpers.noop = function(){},
- uid = helpers.uid = (function(){
- var id=0;
- return function(){
- return "chart-" + id++;
- };
- })(),
- warn = helpers.warn = function(str){
- //Method for warning of errors
- if (window.console && typeof window.console.warn == "function") console.warn(str);
- },
- amd = helpers.amd = (typeof define == 'function' && define.amd),
- //-- Math methods
- isNumber = helpers.isNumber = function(n){
- return !isNaN(parseFloat(n)) && isFinite(n);
- },
- max = helpers.max = function(array){
- return Math.max.apply( Math, array );
- },
- min = helpers.min = function(array){
- return Math.min.apply( Math, array );
- },
- cap = helpers.cap = function(valueToCap,maxValue,minValue){
- if(isNumber(maxValue)) {
- if( valueToCap > maxValue ) {
- return maxValue;
- }
- }
- else if(isNumber(minValue)){
- if ( valueToCap < minValue ){
- return minValue;
- }
- }
- return valueToCap;
- },
- getDecimalPlaces = helpers.getDecimalPlaces = function(num){
- if (num%1!==0 && isNumber(num)){
- return num.toString().split(".")[1].length;
- }
- else {
- return 0;
- }
- },
- toRadians = helpers.radians = function(degrees){
- return degrees * (Math.PI/180);
- },
- // Gets the angle from vertical upright to the point about a centre.
- getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint){
- var distanceFromXCenter = anglePoint.x - centrePoint.x,
- distanceFromYCenter = anglePoint.y - centrePoint.y,
- radialDistanceFromCenter = Math.sqrt( distanceFromXCenter * distanceFromXCenter + distanceFromYCenter * distanceFromYCenter);
-
-
- var angle = Math.PI * 2 + Math.atan2(distanceFromYCenter, distanceFromXCenter);
-
- //If the segment is in the top left quadrant, we need to add another rotation to the angle
- if (distanceFromXCenter < 0 && distanceFromYCenter < 0){
- angle += Math.PI*2;
- }
-
- return {
- angle: angle,
- distance: radialDistanceFromCenter
- };
- },
- aliasPixel = helpers.aliasPixel = function(pixelWidth){
- return (pixelWidth % 2 === 0) ? 0 : 0.5;
- },
- splineCurve = helpers.splineCurve = function(FirstPoint,MiddlePoint,AfterPoint,t){
- //Props to Rob Spencer at scaled innovation for his post on splining between points
- //http://scaledinnovation.com/analytics/splines/aboutSplines.html
- var d01=Math.sqrt(Math.pow(MiddlePoint.x-FirstPoint.x,2)+Math.pow(MiddlePoint.y-FirstPoint.y,2)),
- d12=Math.sqrt(Math.pow(AfterPoint.x-MiddlePoint.x,2)+Math.pow(AfterPoint.y-MiddlePoint.y,2)),
- fa=t*d01/(d01+d12),// scaling factor for triangle Ta
- fb=t*d12/(d01+d12);
- return {
- inner : {
- x : MiddlePoint.x-fa*(AfterPoint.x-FirstPoint.x),
- y : MiddlePoint.y-fa*(AfterPoint.y-FirstPoint.y)
- },
- outer : {
- x: MiddlePoint.x+fb*(AfterPoint.x-FirstPoint.x),
- y : MiddlePoint.y+fb*(AfterPoint.y-FirstPoint.y)
- }
- };
- },
- calculateOrderOfMagnitude = helpers.calculateOrderOfMagnitude = function(val){
- return Math.floor(Math.log(val) / Math.LN10);
- },
- calculateScaleRange = helpers.calculateScaleRange = function(valuesArray, drawingSize, textSize, startFromZero, integersOnly){
-
- //Set a minimum step of two - a point at the top of the graph, and a point at the base
- var minSteps = 2,
- maxSteps = Math.floor(drawingSize/(textSize * 1.5)),
- skipFitting = (minSteps >= maxSteps);
-
- var maxValue = max(valuesArray),
- minValue = min(valuesArray);
-
- // We need some degree of seperation here to calculate the scales if all the values are the same
- // Adding/minusing 0.5 will give us a range of 1.
- if (maxValue === minValue){
- maxValue += 0.5;
- // So we don't end up with a graph with a negative start value if we've said always start from zero
- if (minValue >= 0.5 && !startFromZero){
- minValue -= 0.5;
- }
- else{
- // Make up a whole number above the values
- maxValue += 0.5;
- }
- }
-
- var valueRange = Math.abs(maxValue - minValue),
- rangeOrderOfMagnitude = calculateOrderOfMagnitude(valueRange),
- graphMax = Math.ceil(maxValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
- graphMin = (startFromZero) ? 0 : Math.floor(minValue / (1 * Math.pow(10, rangeOrderOfMagnitude))) * Math.pow(10, rangeOrderOfMagnitude),
- graphRange = graphMax - graphMin,
- stepValue = Math.pow(10, rangeOrderOfMagnitude),
- numberOfSteps = Math.round(graphRange / stepValue);
-
- //If we have more space on the graph we'll use it to give more definition to the data
- while((numberOfSteps > maxSteps || (numberOfSteps * 2) < maxSteps) && !skipFitting) {
- if(numberOfSteps > maxSteps){
- stepValue *=2;
- numberOfSteps = Math.round(graphRange/stepValue);
- // Don't ever deal with a decimal number of steps - cancel fitting and just use the minimum number of steps.
- if (numberOfSteps % 1 !== 0){
- skipFitting = true;
- }
- }
- //We can fit in double the amount of scale points on the scale
- else{
- //If user has declared ints only, and the step value isn't a decimal
- if (integersOnly && rangeOrderOfMagnitude >= 0){
- //If the user has said integers only, we need to check that making the scale more granular wouldn't make it a float
- if(stepValue/2 % 1 === 0){
- stepValue /=2;
- numberOfSteps = Math.round(graphRange/stepValue);
- }
- //If it would make it a float break out of the loop
- else{
- break;
- }
- }
- //If the scale doesn't have to be an int, make the scale more granular anyway.
- else{
- stepValue /=2;
- numberOfSteps = Math.round(graphRange/stepValue);
- }
-
- }
- }
-
- if (skipFitting){
- numberOfSteps = minSteps;
- stepValue = graphRange / numberOfSteps;
- }
-
- return {
- steps : numberOfSteps,
- stepValue : stepValue,
- min : graphMin,
- max : graphMin + (numberOfSteps * stepValue)
- };
-
- },
- /* jshint ignore:start */
- // Blows up jshint errors based on the new Function constructor
- //Templating methods
- //Javascript micro templating by John Resig - source at http://ejohn.org/blog/javascript-micro-templating/
- template = helpers.template = function(templateString, valuesObject){
-
- // If templateString is function rather than string-template - call the function for valuesObject
-
- if(templateString instanceof Function){
- return templateString(valuesObject);
- }
-
- var cache = {};
- function tmpl(str, data){
- // Figure out if we're getting a template, or if we need to
- // load the template - and be sure to cache the result.
- var fn = !/\W/.test(str) ?
- cache[str] = cache[str] :
-
- // Generate a reusable function that will serve as a template
- // generator (and which will be cached).
- new Function("obj",
- "var p=[],print=function(){p.push.apply(p,arguments);};" +
-
- // Introduce the data as local variables using with(){}
- "with(obj){p.push('" +
-
- // Convert the template into pure JavaScript
- str
- .replace(/[\r\t\n]/g, " ")
- .split("<%").join("\t")
- .replace(/((^|%>)[^\t]*)'/g, "$1\r")
- .replace(/\t=(.*?)%>/g, "',$1,'")
- .split("\t").join("');")
- .split("%>").join("p.push('")
- .split("\r").join("\\'") +
- "');}return p.join('');"
- );
-
- // Provide some basic currying to the user
- return data ? fn( data ) : fn;
- }
- return tmpl(templateString,valuesObject);
- },
- /* jshint ignore:end */
- generateLabels = helpers.generateLabels = function(templateString,numberOfSteps,graphMin,stepValue){
- var labelsArray = new Array(numberOfSteps);
- if (labelTemplateString){
- each(labelsArray,function(val,index){
- labelsArray[index] = template(templateString,{value: (graphMin + (stepValue*(index+1)))});
- });
- }
- return labelsArray;
- },
- //--Animation methods
- //Easing functions adapted from Robert Penner's easing equations
- //http://www.robertpenner.com/easing/
- easingEffects = helpers.easingEffects = {
- linear: function (t) {
- return t;
- },
- easeInQuad: function (t) {
- return t * t;
- },
- easeOutQuad: function (t) {
- return -1 * t * (t - 2);
- },
- easeInOutQuad: function (t) {
- if ((t /= 1 / 2) < 1) return 1 / 2 * t * t;
- return -1 / 2 * ((--t) * (t - 2) - 1);
- },
- easeInCubic: function (t) {
- return t * t * t;
- },
- easeOutCubic: function (t) {
- return 1 * ((t = t / 1 - 1) * t * t + 1);
- },
- easeInOutCubic: function (t) {
- if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t;
- return 1 / 2 * ((t -= 2) * t * t + 2);
- },
- easeInQuart: function (t) {
- return t * t * t * t;
- },
- easeOutQuart: function (t) {
- return -1 * ((t = t / 1 - 1) * t * t * t - 1);
- },
- easeInOutQuart: function (t) {
- if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t;
- return -1 / 2 * ((t -= 2) * t * t * t - 2);
- },
- easeInQuint: function (t) {
- return 1 * (t /= 1) * t * t * t * t;
- },
- easeOutQuint: function (t) {
- return 1 * ((t = t / 1 - 1) * t * t * t * t + 1);
- },
- easeInOutQuint: function (t) {
- if ((t /= 1 / 2) < 1) return 1 / 2 * t * t * t * t * t;
- return 1 / 2 * ((t -= 2) * t * t * t * t + 2);
- },
- easeInSine: function (t) {
- return -1 * Math.cos(t / 1 * (Math.PI / 2)) + 1;
- },
- easeOutSine: function (t) {
- return 1 * Math.sin(t / 1 * (Math.PI / 2));
- },
- easeInOutSine: function (t) {
- return -1 / 2 * (Math.cos(Math.PI * t / 1) - 1);
- },
- easeInExpo: function (t) {
- return (t === 0) ? 1 : 1 * Math.pow(2, 10 * (t / 1 - 1));
- },
- easeOutExpo: function (t) {
- return (t === 1) ? 1 : 1 * (-Math.pow(2, -10 * t / 1) + 1);
- },
- easeInOutExpo: function (t) {
- if (t === 0) return 0;
- if (t === 1) return 1;
- if ((t /= 1 / 2) < 1) return 1 / 2 * Math.pow(2, 10 * (t - 1));
- return 1 / 2 * (-Math.pow(2, -10 * --t) + 2);
- },
- easeInCirc: function (t) {
- if (t >= 1) return t;
- return -1 * (Math.sqrt(1 - (t /= 1) * t) - 1);
- },
- easeOutCirc: function (t) {
- return 1 * Math.sqrt(1 - (t = t / 1 - 1) * t);
- },
- easeInOutCirc: function (t) {
- if ((t /= 1 / 2) < 1) return -1 / 2 * (Math.sqrt(1 - t * t) - 1);
- return 1 / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1);
- },
- easeInElastic: function (t) {
- var s = 1.70158;
- var p = 0;
- var a = 1;
- if (t === 0) return 0;
- if ((t /= 1) == 1) return 1;
- if (!p) p = 1 * 0.3;
- if (a < Math.abs(1)) {
- a = 1;
- s = p / 4;
- } else s = p / (2 * Math.PI) * Math.asin(1 / a);
- return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
- },
- easeOutElastic: function (t) {
- var s = 1.70158;
- var p = 0;
- var a = 1;
- if (t === 0) return 0;
- if ((t /= 1) == 1) return 1;
- if (!p) p = 1 * 0.3;
- if (a < Math.abs(1)) {
- a = 1;
- s = p / 4;
- } else s = p / (2 * Math.PI) * Math.asin(1 / a);
- return a * Math.pow(2, -10 * t) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) + 1;
- },
- easeInOutElastic: function (t) {
- var s = 1.70158;
- var p = 0;
- var a = 1;
- if (t === 0) return 0;
- if ((t /= 1 / 2) == 2) return 1;
- if (!p) p = 1 * (0.3 * 1.5);
- if (a < Math.abs(1)) {
- a = 1;
- s = p / 4;
- } else s = p / (2 * Math.PI) * Math.asin(1 / a);
- if (t < 1) return -0.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p));
- return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * 1 - s) * (2 * Math.PI) / p) * 0.5 + 1;
- },
- easeInBack: function (t) {
- var s = 1.70158;
- return 1 * (t /= 1) * t * ((s + 1) * t - s);
- },
- easeOutBack: function (t) {
- var s = 1.70158;
- return 1 * ((t = t / 1 - 1) * t * ((s + 1) * t + s) + 1);
- },
- easeInOutBack: function (t) {
- var s = 1.70158;
- if ((t /= 1 / 2) < 1) return 1 / 2 * (t * t * (((s *= (1.525)) + 1) * t - s));
- return 1 / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2);
- },
- easeInBounce: function (t) {
- return 1 - easingEffects.easeOutBounce(1 - t);
- },
- easeOutBounce: function (t) {
- if ((t /= 1) < (1 / 2.75)) {
- return 1 * (7.5625 * t * t);
- } else if (t < (2 / 2.75)) {
- return 1 * (7.5625 * (t -= (1.5 / 2.75)) * t + 0.75);
- } else if (t < (2.5 / 2.75)) {
- return 1 * (7.5625 * (t -= (2.25 / 2.75)) * t + 0.9375);
- } else {
- return 1 * (7.5625 * (t -= (2.625 / 2.75)) * t + 0.984375);
- }
- },
- easeInOutBounce: function (t) {
- if (t < 1 / 2) return easingEffects.easeInBounce(t * 2) * 0.5;
- return easingEffects.easeOutBounce(t * 2 - 1) * 0.5 + 1 * 0.5;
- }
- },
- //Request animation polyfill - http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
- requestAnimFrame = helpers.requestAnimFrame = (function(){
- return window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame ||
- window.oRequestAnimationFrame ||
- window.msRequestAnimationFrame ||
- function(callback) {
- return window.setTimeout(callback, 1000 / 60);
- };
- })(),
- cancelAnimFrame = helpers.cancelAnimFrame = (function(){
- return window.cancelAnimationFrame ||
- window.webkitCancelAnimationFrame ||
- window.mozCancelAnimationFrame ||
- window.oCancelAnimationFrame ||
- window.msCancelAnimationFrame ||
- function(callback) {
- return window.clearTimeout(callback, 1000 / 60);
- };
- })(),
- animationLoop = helpers.animationLoop = function(callback,totalSteps,easingString,onProgress,onComplete,chartInstance){
-
- var currentStep = 0,
- easingFunction = easingEffects[easingString] || easingEffects.linear;
-
- var animationFrame = function(){
- currentStep++;
- var stepDecimal = currentStep/totalSteps;
- var easeDecimal = easingFunction(stepDecimal);
-
- callback.call(chartInstance,easeDecimal,stepDecimal, currentStep);
- onProgress.call(chartInstance,easeDecimal,stepDecimal);
- if (currentStep < totalSteps){
- chartInstance.animationFrame = requestAnimFrame(animationFrame);
- } else{
- onComplete.apply(chartInstance);
- }
- };
- requestAnimFrame(animationFrame);
- },
- //-- DOM methods
- getRelativePosition = helpers.getRelativePosition = function(evt){
- var mouseX, mouseY;
- var e = evt.originalEvent || evt,
- canvas = evt.currentTarget || evt.srcElement,
- boundingRect = canvas.getBoundingClientRect();
-
- if (e.touches){
- mouseX = e.touches[0].clientX - boundingRect.left;
- mouseY = e.touches[0].clientY - boundingRect.top;
-
- }
- else{
- mouseX = e.clientX - boundingRect.left;
- mouseY = e.clientY - boundingRect.top;
- }
-
- return {
- x : mouseX,
- y : mouseY
- };
-
- },
- addEvent = helpers.addEvent = function(node,eventType,method){
- if (node.addEventListener){
- node.addEventListener(eventType,method);
- } else if (node.attachEvent){
- node.attachEvent("on"+eventType, method);
- } else {
- node["on"+eventType] = method;
- }
- },
- removeEvent = helpers.removeEvent = function(node, eventType, handler){
- if (node.removeEventListener){
- node.removeEventListener(eventType, handler, false);
- } else if (node.detachEvent){
- node.detachEvent("on"+eventType,handler);
- } else{
- node["on" + eventType] = noop;
- }
- },
- bindEvents = helpers.bindEvents = function(chartInstance, arrayOfEvents, handler){
- // Create the events object if it's not already present
- if (!chartInstance.events) chartInstance.events = {};
-
- each(arrayOfEvents,function(eventName){
- chartInstance.events[eventName] = function(){
- handler.apply(chartInstance, arguments);
- };
- addEvent(chartInstance.chart.canvas,eventName,chartInstance.events[eventName]);
- });
- },
- unbindEvents = helpers.unbindEvents = function (chartInstance, arrayOfEvents) {
- each(arrayOfEvents, function(handler,eventName){
- removeEvent(chartInstance.chart.canvas, eventName, handler);
- });
- },
- getMaximumWidth = helpers.getMaximumWidth = function(domNode){
- var container = domNode.parentNode;
- // TODO = check cross browser stuff with this.
- return container.clientWidth;
- },
- getMaximumHeight = helpers.getMaximumHeight = function(domNode){
- var container = domNode.parentNode;
- // TODO = check cross browser stuff with this.
- return container.clientHeight;
- },
- getMaximumSize = helpers.getMaximumSize = helpers.getMaximumWidth, // legacy support
- retinaScale = helpers.retinaScale = function(chart){
- var ctx = chart.ctx,
- width = chart.canvas.width,
- height = chart.canvas.height;
-
- if (window.devicePixelRatio) {
- ctx.canvas.style.width = width + "px";
- ctx.canvas.style.height = height + "px";
- ctx.canvas.height = height * window.devicePixelRatio;
- ctx.canvas.width = width * window.devicePixelRatio;
- ctx.scale(window.devicePixelRatio, window.devicePixelRatio);
- }
- },
- //-- Canvas methods
- clear = helpers.clear = function(chart){
- chart.ctx.clearRect(0,0,chart.width,chart.height);
- },
- fontString = helpers.fontString = function(pixelSize,fontStyle,fontFamily){
- return fontStyle + " " + pixelSize+"px " + fontFamily;
- },
- longestText = helpers.longestText = function(ctx,font,arrayOfStrings){
- ctx.font = font;
- var longest = 0;
- each(arrayOfStrings,function(string){
- var textWidth = ctx.measureText(string).width;
- longest = (textWidth > longest) ? textWidth : longest;
- });
- return longest;
- },
- drawRoundedRectangle = helpers.drawRoundedRectangle = function(ctx,x,y,width,height,radius){
- ctx.beginPath();
- ctx.moveTo(x + radius, y);
- ctx.lineTo(x + width - radius, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
- ctx.lineTo(x + width, y + height - radius);
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
- ctx.lineTo(x + radius, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
- ctx.lineTo(x, y + radius);
- ctx.quadraticCurveTo(x, y, x + radius, y);
- ctx.closePath();
- };
-
-
- //Store a reference to each instance - allowing us to globally resize chart instances on window resize.
- //Destroy method on the chart will remove the instance of the chart from this reference.
- Chart.instances = {};
-
- Chart.Type = function(data,options,chart){
- this.options = options;
- this.chart = chart;
- this.id = uid();
- //Add the chart instance to the global namespace
- Chart.instances[this.id] = this;
-
- // Initialize is always called when a chart type is created
- // By default it is a no op, but it should be extended
- if (options.responsive){
- this.resize();
- }
- this.initialize.call(this,data);
- };
-
- //Core methods that'll be a part of every chart type
- extend(Chart.Type.prototype,{
- initialize : function(){return this;},
- clear : function(){
- clear(this.chart);
- return this;
- },
- stop : function(){
- // Stops any current animation loop occuring
- cancelAnimFrame(this.animationFrame);
- return this;
- },
- resize : function(callback){
- this.stop();
- var canvas = this.chart.canvas,
- newWidth = getMaximumWidth(this.chart.canvas),
- newHeight = this.options.maintainAspectRatio ? newWidth / this.chart.aspectRatio : getMaximumHeight(this.chart.canvas);
-
- canvas.width = this.chart.width = newWidth;
- canvas.height = this.chart.height = newHeight;
-
- retinaScale(this.chart);
-
- if (typeof callback === "function"){
- callback.apply(this, Array.prototype.slice.call(arguments, 1));
- }
- return this;
- },
- reflow : noop,
- render : function(reflow){
- if (reflow){
- this.reflow();
- }
- if (this.options.animation && !reflow){
- helpers.animationLoop(
- this.draw,
- this.options.animationSteps,
- this.options.animationEasing,
- this.options.onAnimationProgress,
- this.options.onAnimationComplete,
- this
- );
- }
- else{
- this.draw();
- this.options.onAnimationComplete.call(this);
- }
- return this;
- },
- generateLegend : function(){
- return template(this.options.legendTemplate,this);
- },
- destroy : function(){
- this.clear();
- unbindEvents(this, this.events);
- var canvas = this.chart.canvas;
-
- // Reset canvas height/width attributes starts a fresh with the canvas context
- canvas.width = this.chart.width;
- canvas.height = this.chart.height;
-
- // < IE9 doesn't support removeProperty
- if (canvas.style.removeProperty) {
- canvas.style.removeProperty('width');
- canvas.style.removeProperty('height');
- } else {
- canvas.style.removeAttribute('width');
- canvas.style.removeAttribute('height');
- }
-
- delete Chart.instances[this.id];
- },
- showTooltip : function(ChartElements, forceRedraw){
- // Only redraw the chart if we've actually changed what we're hovering on.
- if (typeof this.activeElements === 'undefined') this.activeElements = [];
-
- var isChanged = (function(Elements){
- var changed = false;
-
- if (Elements.length !== this.activeElements.length){
- changed = true;
- return changed;
- }
-
- each(Elements, function(element, index){
- if (element !== this.activeElements[index]){
- changed = true;
- }
- }, this);
- return changed;
- }).call(this, ChartElements);
-
- if (!isChanged && !forceRedraw){
- return;
- }
- else{
- this.activeElements = ChartElements;
- }
- this.draw();
- if(this.options.customTooltips){
- this.options.customTooltips(false);
- }
- if (ChartElements.length > 0){
- // If we have multiple datasets, show a MultiTooltip for all of the data points at that index
- if (this.datasets && this.datasets.length > 1) {
- var dataArray,
- dataIndex;
-
- for (var i = this.datasets.length - 1; i >= 0; i--) {
- dataArray = this.datasets[i].points || this.datasets[i].bars || this.datasets[i].segments;
- dataIndex = indexOf(dataArray, ChartElements[0]);
- if (dataIndex !== -1){
- break;
- }
- }
- var tooltipLabels = [],
- tooltipColors = [],
- medianPosition = (function(index) {
-
- // Get all the points at that particular index
- var Elements = [],
- dataCollection,
- xPositions = [],
- yPositions = [],
- xMax,
- yMax,
- xMin,
- yMin;
- helpers.each(this.datasets, function(dataset){
- dataCollection = dataset.points || dataset.bars || dataset.segments;
- if (dataCollection[dataIndex] && dataCollection[dataIndex].hasValue()){
- Elements.push(dataCollection[dataIndex]);
- }
- });
-
- helpers.each(Elements, function(element) {
- xPositions.push(element.x);
- yPositions.push(element.y);
-
-
- //Include any colour information about the element
- tooltipLabels.push(helpers.template(this.options.multiTooltipTemplate, element));
- tooltipColors.push({
- fill: element._saved.fillColor || element.fillColor,
- stroke: element._saved.strokeColor || element.strokeColor
- });
-
- }, this);
-
- yMin = min(yPositions);
- yMax = max(yPositions);
-
- xMin = min(xPositions);
- xMax = max(xPositions);
-
- return {
- x: (xMin > this.chart.width/2) ? xMin : xMax,
- y: (yMin + yMax)/2
- };
- }).call(this, dataIndex);
-
- new Chart.MultiTooltip({
- x: medianPosition.x,
- y: medianPosition.y,
- xPadding: this.options.tooltipXPadding,
- yPadding: this.options.tooltipYPadding,
- xOffset: this.options.tooltipXOffset,
- fillColor: this.options.tooltipFillColor,
- textColor: this.options.tooltipFontColor,
- fontFamily: this.options.tooltipFontFamily,
- fontStyle: this.options.tooltipFontStyle,
- fontSize: this.options.tooltipFontSize,
- titleTextColor: this.options.tooltipTitleFontColor,
- titleFontFamily: this.options.tooltipTitleFontFamily,
- titleFontStyle: this.options.tooltipTitleFontStyle,
- titleFontSize: this.options.tooltipTitleFontSize,
- cornerRadius: this.options.tooltipCornerRadius,
- labels: tooltipLabels,
- legendColors: tooltipColors,
- legendColorBackground : this.options.multiTooltipKeyBackground,
- title: ChartElements[0].label,
- chart: this.chart,
- ctx: this.chart.ctx,
- custom: this.options.customTooltips
- }).draw();
-
- } else {
- each(ChartElements, function(Element) {
- var tooltipPosition = Element.tooltipPosition();
- new Chart.Tooltip({
- x: Math.round(tooltipPosition.x),
- y: Math.round(tooltipPosition.y),
- xPadding: this.options.tooltipXPadding,
- yPadding: this.options.tooltipYPadding,
- fillColor: this.options.tooltipFillColor,
- textColor: this.options.tooltipFontColor,
- fontFamily: this.options.tooltipFontFamily,
- fontStyle: this.options.tooltipFontStyle,
- fontSize: this.options.tooltipFontSize,
- caretHeight: this.options.tooltipCaretSize,
- cornerRadius: this.options.tooltipCornerRadius,
- text: template(this.options.tooltipTemplate, Element),
- chart: this.chart,
- custom: this.options.customTooltips
- }).draw();
- }, this);
- }
- }
- return this;
- },
- toBase64Image : function(){
- return this.chart.canvas.toDataURL.apply(this.chart.canvas, arguments);
- }
- });
-
- Chart.Type.extend = function(extensions){
-
- var parent = this;
-
- var ChartType = function(){
- return parent.apply(this,arguments);
- };
-
- //Copy the prototype object of the this class
- ChartType.prototype = clone(parent.prototype);
- //Now overwrite some of the properties in the base class with the new extensions
- extend(ChartType.prototype, extensions);
-
- ChartType.extend = Chart.Type.extend;
-
- if (extensions.name || parent.prototype.name){
-
- var chartName = extensions.name || parent.prototype.name;
- //Assign any potential default values of the new chart type
-
- //If none are defined, we'll use a clone of the chart type this is being extended from.
- //I.e. if we extend a line chart, we'll use the defaults from the line chart if our new chart
- //doesn't define some defaults of their own.
-
- var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {};
-
- Chart.defaults[chartName] = extend(baseDefaults,extensions.defaults);
-
- Chart.types[chartName] = ChartType;
-
- //Register this new chart type in the Chart prototype
- Chart.prototype[chartName] = function(data,options){
- var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {});
- return new ChartType(data,config,this);
- };
- } else{
- warn("Name not provided for this chart, so it hasn't been registered");
- }
- return parent;
- };
-
- Chart.Element = function(configuration){
- extend(this,configuration);
- this.initialize.apply(this,arguments);
- this.save();
- };
- extend(Chart.Element.prototype,{
- initialize : function(){},
- restore : function(props){
- if (!props){
- extend(this,this._saved);
- } else {
- each(props,function(key){
- this[key] = this._saved[key];
- },this);
- }
- return this;
- },
- save : function(){
- this._saved = clone(this);
- delete this._saved._saved;
- return this;
- },
- update : function(newProps){
- each(newProps,function(value,key){
- this._saved[key] = this[key];
- this[key] = value;
- },this);
- return this;
- },
- transition : function(props,ease){
- each(props,function(value,key){
- this[key] = ((value - this._saved[key]) * ease) + this._saved[key];
- },this);
- return this;
- },
- tooltipPosition : function(){
- return {
- x : this.x,
- y : this.y
- };
- },
- hasValue: function(){
- return isNumber(this.value);
- }
- });
-
- Chart.Element.extend = inherits;
-
-
- Chart.Point = Chart.Element.extend({
- display: true,
- inRange: function(chartX,chartY){
- var hitDetectionRange = this.hitDetectionRadius + this.radius;
- return ((Math.pow(chartX-this.x, 2)+Math.pow(chartY-this.y, 2)) < Math.pow(hitDetectionRange,2));
- },
- draw : function(){
- if (this.display){
- var ctx = this.ctx;
- ctx.beginPath();
-
- ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2);
- ctx.closePath();
-
- ctx.strokeStyle = this.strokeColor;
- ctx.lineWidth = this.strokeWidth;
-
- ctx.fillStyle = this.fillColor;
-
- ctx.fill();
- ctx.stroke();
- }
-
-
- //Quick debug for bezier curve splining
- //Highlights control points and the line between them.
- //Handy for dev - stripped in the min version.
-
- // ctx.save();
- // ctx.fillStyle = "black";
- // ctx.strokeStyle = "black"
- // ctx.beginPath();
- // ctx.arc(this.controlPoints.inner.x,this.controlPoints.inner.y, 2, 0, Math.PI*2);
- // ctx.fill();
-
- // ctx.beginPath();
- // ctx.arc(this.controlPoints.outer.x,this.controlPoints.outer.y, 2, 0, Math.PI*2);
- // ctx.fill();
-
- // ctx.moveTo(this.controlPoints.inner.x,this.controlPoints.inner.y);
- // ctx.lineTo(this.x, this.y);
- // ctx.lineTo(this.controlPoints.outer.x,this.controlPoints.outer.y);
- // ctx.stroke();
-
- // ctx.restore();
-
-
-
- }
- });
-
- Chart.Arc = Chart.Element.extend({
- inRange : function(chartX,chartY){
-
- var pointRelativePosition = helpers.getAngleFromPoint(this, {
- x: chartX,
- y: chartY
- });
-
- //Check if within the range of the open/close angle
- var betweenAngles = (pointRelativePosition.angle >= this.startAngle && pointRelativePosition.angle <= this.endAngle),
- withinRadius = (pointRelativePosition.distance >= this.innerRadius && pointRelativePosition.distance <= this.outerRadius);
-
- return (betweenAngles && withinRadius);
- //Ensure within the outside of the arc centre, but inside arc outer
- },
- tooltipPosition : function(){
- var centreAngle = this.startAngle + ((this.endAngle - this.startAngle) / 2),
- rangeFromCentre = (this.outerRadius - this.innerRadius) / 2 + this.innerRadius;
- return {
- x : this.x + (Math.cos(centreAngle) * rangeFromCentre),
- y : this.y + (Math.sin(centreAngle) * rangeFromCentre)
- };
- },
- draw : function(animationPercent){
-
- var easingDecimal = animationPercent || 1;
-
- var ctx = this.ctx;
-
- ctx.beginPath();
-
- ctx.arc(this.x, this.y, this.outerRadius, this.startAngle, this.endAngle);
-
- ctx.arc(this.x, this.y, this.innerRadius, this.endAngle, this.startAngle, true);
-
- ctx.closePath();
- ctx.strokeStyle = this.strokeColor;
- ctx.lineWidth = this.strokeWidth;
-
- ctx.fillStyle = this.fillColor;
-
- ctx.fill();
- ctx.lineJoin = 'bevel';
-
- if (this.showStroke){
- ctx.stroke();
- }
- }
- });
-
- Chart.Rectangle = Chart.Element.extend({
- draw : function(){
- var ctx = this.ctx,
- halfWidth = this.width/2,
- leftX = this.x - halfWidth,
- rightX = this.x + halfWidth,
- top = this.base - (this.base - this.y),
- halfStroke = this.strokeWidth / 2;
-
- // Canvas doesn't allow us to stroke inside the width so we can
- // adjust the sizes to fit if we're setting a stroke on the line
- if (this.showStroke){
- leftX += halfStroke;
- rightX -= halfStroke;
- top += halfStroke;
- }
-
- ctx.beginPath();
-
- ctx.fillStyle = this.fillColor;
- ctx.strokeStyle = this.strokeColor;
- ctx.lineWidth = this.strokeWidth;
-
- // It'd be nice to keep this class totally generic to any rectangle
- // and simply specify which border to miss out.
- ctx.moveTo(leftX, this.base);
- ctx.lineTo(leftX, top);
- ctx.lineTo(rightX, top);
- ctx.lineTo(rightX, this.base);
- ctx.fill();
- if (this.showStroke){
- ctx.stroke();
- }
- },
- height : function(){
- return this.base - this.y;
- },
- inRange : function(chartX,chartY){
- return (chartX >= this.x - this.width/2 && chartX <= this.x + this.width/2) && (chartY >= this.y && chartY <= this.base);
- }
- });
-
- Chart.Tooltip = Chart.Element.extend({
- draw : function(){
-
- var ctx = this.chart.ctx;
-
- ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
-
- this.xAlign = "center";
- this.yAlign = "above";
-
- //Distance between the actual element.y position and the start of the tooltip caret
- var caretPadding = this.caretPadding = 2;
-
- var tooltipWidth = ctx.measureText(this.text).width + 2*this.xPadding,
- tooltipRectHeight = this.fontSize + 2*this.yPadding,
- tooltipHeight = tooltipRectHeight + this.caretHeight + caretPadding;
-
- if (this.x + tooltipWidth/2 >this.chart.width){
- this.xAlign = "left";
- } else if (this.x - tooltipWidth/2 < 0){
- this.xAlign = "right";
- }
-
- if (this.y - tooltipHeight < 0){
- this.yAlign = "below";
- }
-
-
- var tooltipX = this.x - tooltipWidth/2,
- tooltipY = this.y - tooltipHeight;
-
- ctx.fillStyle = this.fillColor;
-
- // Custom Tooltips
- if(this.custom){
- this.custom(this);
- }
- else{
- switch(this.yAlign)
- {
- case "above":
- //Draw a caret above the x/y
- ctx.beginPath();
- ctx.moveTo(this.x,this.y - caretPadding);
- ctx.lineTo(this.x + this.caretHeight, this.y - (caretPadding + this.caretHeight));
- ctx.lineTo(this.x - this.caretHeight, this.y - (caretPadding + this.caretHeight));
- ctx.closePath();
- ctx.fill();
- break;
- case "below":
- tooltipY = this.y + caretPadding + this.caretHeight;
- //Draw a caret below the x/y
- ctx.beginPath();
- ctx.moveTo(this.x, this.y + caretPadding);
- ctx.lineTo(this.x + this.caretHeight, this.y + caretPadding + this.caretHeight);
- ctx.lineTo(this.x - this.caretHeight, this.y + caretPadding + this.caretHeight);
- ctx.closePath();
- ctx.fill();
- break;
- }
-
- switch(this.xAlign)
- {
- case "left":
- tooltipX = this.x - tooltipWidth + (this.cornerRadius + this.caretHeight);
- break;
- case "right":
- tooltipX = this.x - (this.cornerRadius + this.caretHeight);
- break;
- }
-
- drawRoundedRectangle(ctx,tooltipX,tooltipY,tooltipWidth,tooltipRectHeight,this.cornerRadius);
-
- ctx.fill();
-
- ctx.fillStyle = this.textColor;
- ctx.textAlign = "center";
- ctx.textBaseline = "middle";
- ctx.fillText(this.text, tooltipX + tooltipWidth/2, tooltipY + tooltipRectHeight/2);
- }
- }
- });
-
- Chart.MultiTooltip = Chart.Element.extend({
- initialize : function(){
- this.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
-
- this.titleFont = fontString(this.titleFontSize,this.titleFontStyle,this.titleFontFamily);
-
- this.height = (this.labels.length * this.fontSize) + ((this.labels.length-1) * (this.fontSize/2)) + (this.yPadding*2) + this.titleFontSize *1.5;
-
- this.ctx.font = this.titleFont;
-
- var titleWidth = this.ctx.measureText(this.title).width,
- //Label has a legend square as well so account for this.
- labelWidth = longestText(this.ctx,this.font,this.labels) + this.fontSize + 3,
- longestTextWidth = max([labelWidth,titleWidth]);
-
- this.width = longestTextWidth + (this.xPadding*2);
-
-
- var halfHeight = this.height/2;
-
- //Check to ensure the height will fit on the canvas
- if (this.y - halfHeight < 0 ){
- this.y = halfHeight;
- } else if (this.y + halfHeight > this.chart.height){
- this.y = this.chart.height - halfHeight;
- }
-
- //Decide whether to align left or right based on position on canvas
- if (this.x > this.chart.width/2){
- this.x -= this.xOffset + this.width;
- } else {
- this.x += this.xOffset;
- }
-
-
- },
- getLineHeight : function(index){
- var baseLineHeight = this.y - (this.height/2) + this.yPadding,
- afterTitleIndex = index-1;
-
- //If the index is zero, we're getting the title
- if (index === 0){
- return baseLineHeight + this.titleFontSize/2;
- } else{
- return baseLineHeight + ((this.fontSize*1.5*afterTitleIndex) + this.fontSize/2) + this.titleFontSize * 1.5;
- }
-
- },
- draw : function(){
- // Custom Tooltips
- if(this.custom){
- this.custom(this);
- }
- else{
- drawRoundedRectangle(this.ctx,this.x,this.y - this.height/2,this.width,this.height,this.cornerRadius);
- var ctx = this.ctx;
- ctx.fillStyle = this.fillColor;
- ctx.fill();
- ctx.closePath();
-
- ctx.textAlign = "left";
- ctx.textBaseline = "middle";
- ctx.fillStyle = this.titleTextColor;
- ctx.font = this.titleFont;
-
- ctx.fillText(this.title,this.x + this.xPadding, this.getLineHeight(0));
-
- ctx.font = this.font;
- helpers.each(this.labels,function(label,index){
- ctx.fillStyle = this.textColor;
- ctx.fillText(label,this.x + this.xPadding + this.fontSize + 3, this.getLineHeight(index + 1));
-
- //A bit gnarly, but clearing this rectangle breaks when using explorercanvas (clears whole canvas)
- //ctx.clearRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
- //Instead we'll make a white filled block to put the legendColour palette over.
-
- ctx.fillStyle = this.legendColorBackground;
- ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
-
- ctx.fillStyle = this.legendColors[index].fill;
- ctx.fillRect(this.x + this.xPadding, this.getLineHeight(index + 1) - this.fontSize/2, this.fontSize, this.fontSize);
-
-
- },this);
- }
- }
- });
-
- Chart.Scale = Chart.Element.extend({
- initialize : function(){
- this.fit();
- },
- buildYLabels : function(){
- this.yLabels = [];
-
- var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
-
- for (var i=0; i<=this.steps; i++){
- this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
- }
- this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx,this.font,this.yLabels) : 0;
- },
- addXLabel : function(label){
- this.xLabels.push(label);
- this.valuesCount++;
- this.fit();
- },
- removeXLabel : function(){
- this.xLabels.shift();
- this.valuesCount--;
- this.fit();
- },
- // Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use
- fit: function(){
- // First we need the width of the yLabels, assuming the xLabels aren't rotated
-
- // To do that we need the base line at the top and base of the chart, assuming there is no x label rotation
- this.startPoint = (this.display) ? this.fontSize : 0;
- this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels
-
- // Apply padding settings to the start and end point.
- this.startPoint += this.padding;
- this.endPoint -= this.padding;
-
- // Cache the starting height, so can determine if we need to recalculate the scale yAxis
- var cachedHeight = this.endPoint - this.startPoint,
- cachedYLabelWidth;
-
- // Build the current yLabels so we have an idea of what size they'll be to start
- /*
- * This sets what is returned from calculateScaleRange as static properties of this class:
- *
- this.steps;
- this.stepValue;
- this.min;
- this.max;
- *
- */
- this.calculateYRange(cachedHeight);
-
- // With these properties set we can now build the array of yLabels
- // and also the width of the largest yLabel
- this.buildYLabels();
-
- this.calculateXLabelRotation();
-
- while((cachedHeight > this.endPoint - this.startPoint)){
- cachedHeight = this.endPoint - this.startPoint;
- cachedYLabelWidth = this.yLabelWidth;
-
- this.calculateYRange(cachedHeight);
- this.buildYLabels();
-
- // Only go through the xLabel loop again if the yLabel width has changed
- if (cachedYLabelWidth < this.yLabelWidth){
- this.calculateXLabelRotation();
- }
- }
-
- },
- calculateXLabelRotation : function(){
- //Get the width of each grid by calculating the difference
- //between x offsets between 0 and 1.
-
- this.ctx.font = this.font;
-
- var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
- lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
- firstRotated,
- lastRotated;
-
-
- this.xScalePaddingRight = lastWidth/2 + 3;
- this.xScalePaddingLeft = (firstWidth/2 > this.yLabelWidth + 10) ? firstWidth/2 : this.yLabelWidth + 10;
-
- this.xLabelRotation = 0;
- if (this.display){
- var originalLabelWidth = longestText(this.ctx,this.font,this.xLabels),
- cosRotation,
- firstRotatedWidth;
- this.xLabelWidth = originalLabelWidth;
- //Allow 3 pixels x2 padding either side for label readability
- var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
-
- //Max label rotate should be 90 - also act as a loop counter
- while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)){
- cosRotation = Math.cos(toRadians(this.xLabelRotation));
-
- firstRotated = cosRotation * firstWidth;
- lastRotated = cosRotation * lastWidth;
-
- // We're right aligning the text now.
- if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8){
- this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
- }
- this.xScalePaddingRight = this.fontSize/2;
-
-
- this.xLabelRotation++;
- this.xLabelWidth = cosRotation * originalLabelWidth;
-
- }
- if (this.xLabelRotation > 0){
- this.endPoint -= Math.sin(toRadians(this.xLabelRotation))*originalLabelWidth + 3;
- }
- }
- else{
- this.xLabelWidth = 0;
- this.xScalePaddingRight = this.padding;
- this.xScalePaddingLeft = this.padding;
- }
-
- },
- // Needs to be overidden in each Chart type
- // Otherwise we need to pass all the data into the scale class
- calculateYRange: noop,
- drawingArea: function(){
- return this.startPoint - this.endPoint;
- },
- calculateY : function(value){
- var scalingFactor = this.drawingArea() / (this.min - this.max);
- return this.endPoint - (scalingFactor * (value - this.min));
- },
- calculateX : function(index){
- var isRotated = (this.xLabelRotation > 0),
- // innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
- innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
- valueWidth = innerWidth/Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),
- valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
-
- if (this.offsetGridLines){
- valueOffset += (valueWidth/2);
- }
-
- return Math.round(valueOffset);
- },
- update : function(newProps){
- helpers.extend(this, newProps);
- this.fit();
- },
- draw : function(){
- var ctx = this.ctx,
- yLabelGap = (this.endPoint - this.startPoint) / this.steps,
- xStart = Math.round(this.xScalePaddingLeft);
- if (this.display){
- ctx.fillStyle = this.textColor;
- ctx.font = this.font;
- each(this.yLabels,function(labelString,index){
- var yLabelCenter = this.endPoint - (yLabelGap * index),
- linePositionY = Math.round(yLabelCenter),
- drawHorizontalLine = this.showHorizontalLines;
-
- ctx.textAlign = "right";
- ctx.textBaseline = "middle";
- if (this.showLabels){
- ctx.fillText(labelString,xStart - 10,yLabelCenter);
- }
-
- // This is X axis, so draw it
- if (index === 0 && !drawHorizontalLine){
- drawHorizontalLine = true;
- }
-
- if (drawHorizontalLine){
- ctx.beginPath();
- }
-
- if (index > 0){
- // This is a grid line in the centre, so drop that
- ctx.lineWidth = this.gridLineWidth;
- ctx.strokeStyle = this.gridLineColor;
- } else {
- // This is the first line on the scale
- ctx.lineWidth = this.lineWidth;
- ctx.strokeStyle = this.lineColor;
- }
-
- linePositionY += helpers.aliasPixel(ctx.lineWidth);
-
- if(drawHorizontalLine){
- ctx.moveTo(xStart, linePositionY);
- ctx.lineTo(this.width, linePositionY);
- ctx.stroke();
- ctx.closePath();
- }
-
- ctx.lineWidth = this.lineWidth;
- ctx.strokeStyle = this.lineColor;
- ctx.beginPath();
- ctx.moveTo(xStart - 5, linePositionY);
- ctx.lineTo(xStart, linePositionY);
- ctx.stroke();
- ctx.closePath();
-
- },this);
-
- each(this.xLabels,function(label,index){
- var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
- // Check to see if line/bar here and decide where to place the line
- linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
- isRotated = (this.xLabelRotation > 0),
- drawVerticalLine = this.showVerticalLines;
-
- // This is Y axis, so draw it
- if (index === 0 && !drawVerticalLine){
- drawVerticalLine = true;
- }
-
- if (drawVerticalLine){
- ctx.beginPath();
- }
-
- if (index > 0){
- // This is a grid line in the centre, so drop that
- ctx.lineWidth = this.gridLineWidth;
- ctx.strokeStyle = this.gridLineColor;
- } else {
- // This is the first line on the scale
- ctx.lineWidth = this.lineWidth;
- ctx.strokeStyle = this.lineColor;
- }
-
- if (drawVerticalLine){
- ctx.moveTo(linePos,this.endPoint);
- ctx.lineTo(linePos,this.startPoint - 3);
- ctx.stroke();
- ctx.closePath();
- }
-
-
- ctx.lineWidth = this.lineWidth;
- ctx.strokeStyle = this.lineColor;
-
-
- // Small lines at the bottom of the base grid line
- ctx.beginPath();
- ctx.moveTo(linePos,this.endPoint);
- ctx.lineTo(linePos,this.endPoint + 5);
- ctx.stroke();
- ctx.closePath();
-
- ctx.save();
- ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8);
- ctx.rotate(toRadians(this.xLabelRotation)*-1);
- ctx.font = this.font;
- ctx.textAlign = (isRotated) ? "right" : "center";
- ctx.textBaseline = (isRotated) ? "middle" : "top";
- ctx.fillText(label, 0, 0);
- ctx.restore();
- },this);
-
- }
- }
-
- });
-
- Chart.RadialScale = Chart.Element.extend({
- initialize: function(){
- this.size = min([this.height, this.width]);
- this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
- },
- calculateCenterOffset: function(value){
- // Take into account half font size + the yPadding of the top value
- var scalingFactor = this.drawingArea / (this.max - this.min);
-
- return (value - this.min) * scalingFactor;
- },
- update : function(){
- if (!this.lineArc){
- this.setScaleSize();
- } else {
- this.drawingArea = (this.display) ? (this.size/2) - (this.fontSize/2 + this.backdropPaddingY) : (this.size/2);
- }
- this.buildYLabels();
- },
- buildYLabels: function(){
- this.yLabels = [];
-
- var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
-
- for (var i=0; i<=this.steps; i++){
- this.yLabels.push(template(this.templateString,{value:(this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)}));
- }
- },
- getCircumference : function(){
- return ((Math.PI*2) / this.valuesCount);
- },
- setScaleSize: function(){
- /*
- * Right, this is really confusing and there is a lot of maths going on here
- * The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
- *
- * Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
- *
- * Solution:
- *
- * We assume the radius of the polygon is half the size of the canvas at first
- * at each index we check if the text overlaps.
- *
- * Where it does, we store that angle and that index.
- *
- * After finding the largest index and angle we calculate how much we need to remove
- * from the shape radius to move the point inwards by that x.
- *
- * We average the left and right distances to get the maximum shape radius that can fit in the box
- * along with labels.
- *
- * Once we have that, we can find the centre point for the chart, by taking the x text protrusion
- * on each side, removing that from the size, halving it and adding the left x protrusion width.
- *
- * This will mean we have a shape fitted to the canvas, as large as it can be with the labels
- * and position it in the most space efficient manner
- *
- * https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
- */
-
-
- // Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
- // Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
- var largestPossibleRadius = min([(this.height/2 - this.pointLabelFontSize - 5), this.width/2]),
- pointPosition,
- i,
- textWidth,
- halfTextWidth,
- furthestRight = this.width,
- furthestRightIndex,
- furthestRightAngle,
- furthestLeft = 0,
- furthestLeftIndex,
- furthestLeftAngle,
- xProtrusionLeft,
- xProtrusionRight,
- radiusReductionRight,
- radiusReductionLeft,
- maxWidthRadius;
- this.ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
- for (i=0;i<this.valuesCount;i++){
- // 5px to space the text slightly out - similar to what we do in the draw function.
- pointPosition = this.getPointPosition(i, largestPossibleRadius);
- textWidth = this.ctx.measureText(template(this.templateString, { value: this.labels[i] })).width + 5;
- if (i === 0 || i === this.valuesCount/2){
- // If we're at index zero, or exactly the middle, we're at exactly the top/bottom
- // of the radar chart, so text will be aligned centrally, so we'll half it and compare
- // w/left and right text sizes
- halfTextWidth = textWidth/2;
- if (pointPosition.x + halfTextWidth > furthestRight) {
- furthestRight = pointPosition.x + halfTextWidth;
- furthestRightIndex = i;
- }
- if (pointPosition.x - halfTextWidth < furthestLeft) {
- furthestLeft = pointPosition.x - halfTextWidth;
- furthestLeftIndex = i;
- }
- }
- else if (i < this.valuesCount/2) {
- // Less than half the values means we'll left align the text
- if (pointPosition.x + textWidth > furthestRight) {
- furthestRight = pointPosition.x + textWidth;
- furthestRightIndex = i;
- }
- }
- else if (i > this.valuesCount/2){
- // More than half the values means we'll right align the text
- if (pointPosition.x - textWidth < furthestLeft) {
- furthestLeft = pointPosition.x - textWidth;
- furthestLeftIndex = i;
- }
- }
- }
-
- xProtrusionLeft = furthestLeft;
-
- xProtrusionRight = Math.ceil(furthestRight - this.width);
-
- furthestRightAngle = this.getIndexAngle(furthestRightIndex);
-
- furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
-
- radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI/2);
-
- radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI/2);
-
- // Ensure we actually need to reduce the size of the chart
- radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
- radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
-
- this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight)/2;
-
- //this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
- this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
-
- },
- setCenterPoint: function(leftMovement, rightMovement){
-
- var maxRight = this.width - rightMovement - this.drawingArea,
- maxLeft = leftMovement + this.drawingArea;
-
- this.xCenter = (maxLeft + maxRight)/2;
- // Always vertically in the centre as the text height doesn't change
- this.yCenter = (this.height/2);
- },
-
- getIndexAngle : function(index){
- var angleMultiplier = (Math.PI * 2) / this.valuesCount;
- // Start from the top instead of right, so remove a quarter of the circle
-
- return index * angleMultiplier - (Math.PI/2);
- },
- getPointPosition : function(index, distanceFromCenter){
- var thisAngle = this.getIndexAngle(index);
- return {
- x : (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
- y : (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
- };
- },
- draw: function(){
- if (this.display){
- var ctx = this.ctx;
- each(this.yLabels, function(label, index){
- // Don't draw a centre value
- if (index > 0){
- var yCenterOffset = index * (this.drawingArea/this.steps),
- yHeight = this.yCenter - yCenterOffset,
- pointPosition;
-
- // Draw circular lines around the scale
- if (this.lineWidth > 0){
- ctx.strokeStyle = this.lineColor;
- ctx.lineWidth = this.lineWidth;
-
- if(this.lineArc){
- ctx.beginPath();
- ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI*2);
- ctx.closePath();
- ctx.stroke();
- } else{
- ctx.beginPath();
- for (var i=0;i<this.valuesCount;i++)
- {
- pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue)));
- if (i === 0){
- ctx.moveTo(pointPosition.x, pointPosition.y);
- } else {
- ctx.lineTo(pointPosition.x, pointPosition.y);
- }
- }
- ctx.closePath();
- ctx.stroke();
- }
- }
- if(this.showLabels){
- ctx.font = fontString(this.fontSize,this.fontStyle,this.fontFamily);
- if (this.showLabelBackdrop){
- var labelWidth = ctx.measureText(label).width;
- ctx.fillStyle = this.backdropColor;
- ctx.fillRect(
- this.xCenter - labelWidth/2 - this.backdropPaddingX,
- yHeight - this.fontSize/2 - this.backdropPaddingY,
- labelWidth + this.backdropPaddingX*2,
- this.fontSize + this.backdropPaddingY*2
- );
- }
- ctx.textAlign = 'center';
- ctx.textBaseline = "middle";
- ctx.fillStyle = this.fontColor;
- ctx.fillText(label, this.xCenter, yHeight);
- }
- }
- }, this);
-
- if (!this.lineArc){
- ctx.lineWidth = this.angleLineWidth;
- ctx.strokeStyle = this.angleLineColor;
- for (var i = this.valuesCount - 1; i >= 0; i--) {
- if (this.angleLineWidth > 0){
- var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));
- ctx.beginPath();
- ctx.moveTo(this.xCenter, this.yCenter);
- ctx.lineTo(outerPosition.x, outerPosition.y);
- ctx.stroke();
- ctx.closePath();
- }
- // Extra 3px out for some label spacing
- var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
- ctx.font = fontString(this.pointLabelFontSize,this.pointLabelFontStyle,this.pointLabelFontFamily);
- ctx.fillStyle = this.pointLabelFontColor;
-
- var labelsCount = this.labels.length,
- halfLabelsCount = this.labels.length/2,
- quarterLabelsCount = halfLabelsCount/2,
- upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
- exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
- if (i === 0){
- ctx.textAlign = 'center';
- } else if(i === halfLabelsCount){
- ctx.textAlign = 'center';
- } else if (i < halfLabelsCount){
- ctx.textAlign = 'left';
- } else {
- ctx.textAlign = 'right';
- }
-
- // Set the correct text baseline based on outer positioning
- if (exactQuarter){
- ctx.textBaseline = 'middle';
- } else if (upperHalf){
- ctx.textBaseline = 'bottom';
- } else {
- ctx.textBaseline = 'top';
- }
-
- ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
- }
- }
- }
- }
- });
-
- // Attach global event to resize each chart instance when the browser resizes
- helpers.addEvent(window, "resize", (function(){
- // Basic debounce of resize function so it doesn't hurt performance when resizing browser.
- var timeout;
- return function(){
- clearTimeout(timeout);
- timeout = setTimeout(function(){
- each(Chart.instances,function(instance){
- // If the responsive flag is set in the chart instance config
- // Cascade the resize event down to the chart.
- if (instance.options.responsive){
- instance.resize(instance.render, true);
- }
- });
- }, 50);
- };
- })());
-
-
- if (amd) {
- define(function(){
- return Chart;
- });
- } else if (typeof module === 'object' && module.exports) {
- module.exports = Chart;
- }
-
- root.Chart = Chart;
-
- Chart.noConflict = function(){
- root.Chart = previous;
- return Chart;
- };
-
-}).call(this);
-
-(function(){
- "use strict";
-
- var root = this,
- Chart = root.Chart,
- helpers = Chart.helpers;
-
-
- var defaultConfig = {
- //Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
- scaleBeginAtZero : true,
-
- //Boolean - Whether grid lines are shown across the chart
- scaleShowGridLines : true,
-
- //String - Colour of the grid lines
- scaleGridLineColor : "rgba(0,0,0,.05)",
-
- //Number - Width of the grid lines
- scaleGridLineWidth : 1,
-
- //Boolean - Whether to show horizontal lines (except X axis)
- scaleShowHorizontalLines: true,
-
- //Boolean - Whether to show vertical lines (except Y axis)
- scaleShowVerticalLines: true,
-
- //Boolean - If there is a stroke on each bar
- barShowStroke : true,
-
- //Number - Pixel width of the bar stroke
- barStrokeWidth : 2,
-
- //Number - Spacing between each of the X value sets
- barValueSpacing : 5,
-
- //Number - Spacing between data sets within X values
- barDatasetSpacing : 1,
-
- //String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].fillColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
-
- };
-
-
- Chart.Type.extend({
- name: "Bar",
- defaults : defaultConfig,
- initialize: function(data){
-
- //Expose options as a scope variable here so we can access it in the ScaleClass
- var options = this.options;
-
- this.ScaleClass = Chart.Scale.extend({
- offsetGridLines : true,
- calculateBarX : function(datasetCount, datasetIndex, barIndex){
- //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
- var xWidth = this.calculateBaseWidth(),
- xAbsolute = this.calculateX(barIndex) - (xWidth/2),
- barWidth = this.calculateBarWidth(datasetCount);
-
- return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth/2;
- },
- calculateBaseWidth : function(){
- return (this.calculateX(1) - this.calculateX(0)) - (2*options.barValueSpacing);
- },
- calculateBarWidth : function(datasetCount){
- //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
- var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
-
- return (baseWidth / datasetCount);
- }
- });
-
- this.datasets = [];
-
- //Set up tooltip events on the chart
- if (this.options.showTooltips){
- helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
- var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];
-
- this.eachBars(function(bar){
- bar.restore(['fillColor', 'strokeColor']);
- });
- helpers.each(activeBars, function(activeBar){
- activeBar.fillColor = activeBar.highlightFill;
- activeBar.strokeColor = activeBar.highlightStroke;
- });
- this.showTooltip(activeBars);
- });
- }
-
- //Declare the extension of the default point, to cater for the options passed in to the constructor
- this.BarClass = Chart.Rectangle.extend({
- strokeWidth : this.options.barStrokeWidth,
- showStroke : this.options.barShowStroke,
- ctx : this.chart.ctx
- });
-
- //Iterate through each of the datasets, and build this into a property of the chart
- helpers.each(data.datasets,function(dataset,datasetIndex){
-
- var datasetObject = {
- label : dataset.label || null,
- fillColor : dataset.fillColor,
- strokeColor : dataset.strokeColor,
- bars : []
- };
-
- this.datasets.push(datasetObject);
-
- helpers.each(dataset.data,function(dataPoint,index){
- //Add a new point for each piece of data, passing any required data to draw.
- datasetObject.bars.push(new this.BarClass({
- value : dataPoint,
- label : data.labels[index],
- datasetLabel: dataset.label,
- strokeColor : dataset.strokeColor,
- fillColor : dataset.fillColor,
- highlightFill : dataset.highlightFill || dataset.fillColor,
- highlightStroke : dataset.highlightStroke || dataset.strokeColor
- }));
- },this);
-
- },this);
-
- this.buildScale(data.labels);
-
- this.BarClass.prototype.base = this.scale.endPoint;
-
- this.eachBars(function(bar, index, datasetIndex){
- helpers.extend(bar, {
- width : this.scale.calculateBarWidth(this.datasets.length),
- x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
- y: this.scale.endPoint
- });
- bar.save();
- }, this);
-
- this.render();
- },
- update : function(){
- this.scale.update();
- // Reset any highlight colours before updating.
- helpers.each(this.activeElements, function(activeElement){
- activeElement.restore(['fillColor', 'strokeColor']);
- });
-
- this.eachBars(function(bar){
- bar.save();
- });
- this.render();
- },
- eachBars : function(callback){
- helpers.each(this.datasets,function(dataset, datasetIndex){
- helpers.each(dataset.bars, callback, this, datasetIndex);
- },this);
- },
- getBarsAtEvent : function(e){
- var barsArray = [],
- eventPosition = helpers.getRelativePosition(e),
- datasetIterator = function(dataset){
- barsArray.push(dataset.bars[barIndex]);
- },
- barIndex;
-
- for (var datasetIndex = 0; datasetIndex < this.datasets.length; datasetIndex++) {
- for (barIndex = 0; barIndex < this.datasets[datasetIndex].bars.length; barIndex++) {
- if (this.datasets[datasetIndex].bars[barIndex].inRange(eventPosition.x,eventPosition.y)){
- helpers.each(this.datasets, datasetIterator);
- return barsArray;
- }
- }
- }
-
- return barsArray;
- },
- buildScale : function(labels){
- var self = this;
-
- var dataTotal = function(){
- var values = [];
- self.eachBars(function(bar){
- values.push(bar.value);
- });
- return values;
- };
-
- var scaleOptions = {
- templateString : this.options.scaleLabel,
- height : this.chart.height,
- width : this.chart.width,
- ctx : this.chart.ctx,
- textColor : this.options.scaleFontColor,
- fontSize : this.options.scaleFontSize,
- fontStyle : this.options.scaleFontStyle,
- fontFamily : this.options.scaleFontFamily,
- valuesCount : labels.length,
- beginAtZero : this.options.scaleBeginAtZero,
- integersOnly : this.options.scaleIntegersOnly,
- calculateYRange: function(currentHeight){
- var updatedRanges = helpers.calculateScaleRange(
- dataTotal(),
- currentHeight,
- this.fontSize,
- this.beginAtZero,
- this.integersOnly
- );
- helpers.extend(this, updatedRanges);
- },
- xLabels : labels,
- font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
- lineWidth : this.options.scaleLineWidth,
- lineColor : this.options.scaleLineColor,
- showHorizontalLines : this.options.scaleShowHorizontalLines,
- showVerticalLines : this.options.scaleShowVerticalLines,
- gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
- gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
- padding : (this.options.showScale) ? 0 : (this.options.barShowStroke) ? this.options.barStrokeWidth : 0,
- showLabels : this.options.scaleShowLabels,
- display : this.options.showScale
- };
-
- if (this.options.scaleOverride){
- helpers.extend(scaleOptions, {
- calculateYRange: helpers.noop,
- steps: this.options.scaleSteps,
- stepValue: this.options.scaleStepWidth,
- min: this.options.scaleStartValue,
- max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
- });
- }
-
- this.scale = new this.ScaleClass(scaleOptions);
- },
- addData : function(valuesArray,label){
- //Map the values array for each of the datasets
- helpers.each(valuesArray,function(value,datasetIndex){
- //Add a new point for each piece of data, passing any required data to draw.
- this.datasets[datasetIndex].bars.push(new this.BarClass({
- value : value,
- label : label,
- x: this.scale.calculateBarX(this.datasets.length, datasetIndex, this.scale.valuesCount+1),
- y: this.scale.endPoint,
- width : this.scale.calculateBarWidth(this.datasets.length),
- base : this.scale.endPoint,
- strokeColor : this.datasets[datasetIndex].strokeColor,
- fillColor : this.datasets[datasetIndex].fillColor
- }));
- },this);
-
- this.scale.addXLabel(label);
- //Then re-render the chart.
- this.update();
- },
- removeData : function(){
- this.scale.removeXLabel();
- //Then re-render the chart.
- helpers.each(this.datasets,function(dataset){
- dataset.bars.shift();
- },this);
- this.update();
- },
- reflow : function(){
- helpers.extend(this.BarClass.prototype,{
- y: this.scale.endPoint,
- base : this.scale.endPoint
- });
- var newScaleProps = helpers.extend({
- height : this.chart.height,
- width : this.chart.width
- });
- this.scale.update(newScaleProps);
- },
- draw : function(ease){
- var easingDecimal = ease || 1;
- this.clear();
-
- var ctx = this.chart.ctx;
-
- this.scale.draw(easingDecimal);
-
- //Draw all the bars for each dataset
- helpers.each(this.datasets,function(dataset,datasetIndex){
- helpers.each(dataset.bars,function(bar,index){
- if (bar.hasValue()){
- bar.base = this.scale.endPoint;
- //Transition then draw
- bar.transition({
- x : this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
- y : this.scale.calculateY(bar.value),
- width : this.scale.calculateBarWidth(this.datasets.length)
- }, easingDecimal).draw();
- }
- },this);
-
- },this);
- }
- });
-
-
-}).call(this);
-
-(function(){
- "use strict";
-
- var root = this,
- Chart = root.Chart,
- //Cache a local reference to Chart.helpers
- helpers = Chart.helpers;
-
- var defaultConfig = {
- //Boolean - Whether we should show a stroke on each segment
- segmentShowStroke : true,
-
- //String - The colour of each segment stroke
- segmentStrokeColor : "#fff",
-
- //Number - The width of each segment stroke
- segmentStrokeWidth : 2,
-
- //The percentage of the chart that we cut out of the middle.
- percentageInnerCutout : 50,
-
- //Number - Amount of animation steps
- animationSteps : 100,
-
- //String - Animation easing effect
- animationEasing : "easeOutBounce",
-
- //Boolean - Whether we animate the rotation of the Doughnut
- animateRotate : true,
-
- //Boolean - Whether we animate scaling the Doughnut from the centre
- animateScale : false,
-
- //String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
-
- };
-
-
- Chart.Type.extend({
- //Passing in a name registers this chart in the Chart namespace
- name: "Doughnut",
- //Providing a defaults will also register the deafults in the chart namespace
- defaults : defaultConfig,
- //Initialize is fired when the chart is initialized - Data is passed in as a parameter
- //Config is automatically merged by the core of Chart.js, and is available at this.options
- initialize: function(data){
-
- //Declare segments as a static property to prevent inheriting across the Chart type prototype
- this.segments = [];
- this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
-
- this.SegmentArc = Chart.Arc.extend({
- ctx : this.chart.ctx,
- x : this.chart.width/2,
- y : this.chart.height/2
- });
-
- //Set up tooltip events on the chart
- if (this.options.showTooltips){
- helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
- var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
-
- helpers.each(this.segments,function(segment){
- segment.restore(["fillColor"]);
- });
- helpers.each(activeSegments,function(activeSegment){
- activeSegment.fillColor = activeSegment.highlightColor;
- });
- this.showTooltip(activeSegments);
- });
- }
- this.calculateTotal(data);
-
- helpers.each(data,function(datapoint, index){
- this.addData(datapoint, index, true);
- },this);
-
- this.render();
- },
- getSegmentsAtEvent : function(e){
- var segmentsArray = [];
-
- var location = helpers.getRelativePosition(e);
-
- helpers.each(this.segments,function(segment){
- if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
- },this);
- return segmentsArray;
- },
- addData : function(segment, atIndex, silent){
- var index = atIndex || this.segments.length;
- this.segments.splice(index, 0, new this.SegmentArc({
- value : segment.value,
- outerRadius : (this.options.animateScale) ? 0 : this.outerRadius,
- innerRadius : (this.options.animateScale) ? 0 : (this.outerRadius/100) * this.options.percentageInnerCutout,
- fillColor : segment.color,
- highlightColor : segment.highlight || segment.color,
- showStroke : this.options.segmentShowStroke,
- strokeWidth : this.options.segmentStrokeWidth,
- strokeColor : this.options.segmentStrokeColor,
- startAngle : Math.PI * 1.5,
- circumference : (this.options.animateRotate) ? 0 : this.calculateCircumference(segment.value),
- label : segment.label
- }));
- if (!silent){
- this.reflow();
- this.update();
- }
- },
- calculateCircumference : function(value){
- return (Math.PI*2)*(Math.abs(value) / this.total);
- },
- calculateTotal : function(data){
- this.total = 0;
- helpers.each(data,function(segment){
- this.total += Math.abs(segment.value);
- },this);
- },
- update : function(){
- this.calculateTotal(this.segments);
-
- // Reset any highlight colours before updating.
- helpers.each(this.activeElements, function(activeElement){
- activeElement.restore(['fillColor']);
- });
-
- helpers.each(this.segments,function(segment){
- segment.save();
- });
- this.render();
- },
-
- removeData: function(atIndex){
- var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
- this.segments.splice(indexToDelete, 1);
- this.reflow();
- this.update();
- },
-
- reflow : function(){
- helpers.extend(this.SegmentArc.prototype,{
- x : this.chart.width/2,
- y : this.chart.height/2
- });
- this.outerRadius = (helpers.min([this.chart.width,this.chart.height]) - this.options.segmentStrokeWidth/2)/2;
- helpers.each(this.segments, function(segment){
- segment.update({
- outerRadius : this.outerRadius,
- innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
- });
- }, this);
- },
- draw : function(easeDecimal){
- var animDecimal = (easeDecimal) ? easeDecimal : 1;
- this.clear();
- helpers.each(this.segments,function(segment,index){
- segment.transition({
- circumference : this.calculateCircumference(segment.value),
- outerRadius : this.outerRadius,
- innerRadius : (this.outerRadius/100) * this.options.percentageInnerCutout
- },animDecimal);
-
- segment.endAngle = segment.startAngle + segment.circumference;
-
- segment.draw();
- if (index === 0){
- segment.startAngle = Math.PI * 1.5;
- }
- //Check to see if it's the last segment, if not get the next and update the start angle
- if (index < this.segments.length-1){
- this.segments[index+1].startAngle = segment.endAngle;
- }
- },this);
-
- }
- });
-
- Chart.types.Doughnut.extend({
- name : "Pie",
- defaults : helpers.merge(defaultConfig,{percentageInnerCutout : 0})
- });
-
-}).call(this);
-(function(){
- "use strict";
-
- var root = this,
- Chart = root.Chart,
- helpers = Chart.helpers;
-
- var defaultConfig = {
-
- ///Boolean - Whether grid lines are shown across the chart
- scaleShowGridLines : true,
-
- //String - Colour of the grid lines
- scaleGridLineColor : "rgba(0,0,0,.05)",
-
- //Number - Width of the grid lines
- scaleGridLineWidth : 1,
-
- //Boolean - Whether to show horizontal lines (except X axis)
- scaleShowHorizontalLines: true,
-
- //Boolean - Whether to show vertical lines (except Y axis)
- scaleShowVerticalLines: true,
-
- //Boolean - Whether the line is curved between points
- bezierCurve : true,
-
- //Number - Tension of the bezier curve between points
- bezierCurveTension : 0.4,
-
- //Boolean - Whether to show a dot for each point
- pointDot : true,
-
- //Number - Radius of each point dot in pixels
- pointDotRadius : 4,
-
- //Number - Pixel width of point dot stroke
- pointDotStrokeWidth : 1,
-
- //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
- pointHitDetectionRadius : 20,
-
- //Boolean - Whether to show a stroke for datasets
- datasetStroke : true,
-
- //Number - Pixel width of dataset stroke
- datasetStrokeWidth : 2,
-
- //Boolean - Whether to fill the dataset with a colour
- datasetFill : true,
-
- //String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
-
- };
-
-
- Chart.Type.extend({
- name: "Line",
- defaults : defaultConfig,
- initialize: function(data){
- //Declare the extension of the default point, to cater for the options passed in to the constructor
- this.PointClass = Chart.Point.extend({
- strokeWidth : this.options.pointDotStrokeWidth,
- radius : this.options.pointDotRadius,
- display: this.options.pointDot,
- hitDetectionRadius : this.options.pointHitDetectionRadius,
- ctx : this.chart.ctx,
- inRange : function(mouseX){
- return (Math.pow(mouseX-this.x, 2) < Math.pow(this.radius + this.hitDetectionRadius,2));
- }
- });
-
- this.datasets = [];
-
- //Set up tooltip events on the chart
- if (this.options.showTooltips){
- helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
- var activePoints = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
- this.eachPoints(function(point){
- point.restore(['fillColor', 'strokeColor']);
- });
- helpers.each(activePoints, function(activePoint){
- activePoint.fillColor = activePoint.highlightFill;
- activePoint.strokeColor = activePoint.highlightStroke;
- });
- this.showTooltip(activePoints);
- });
- }
-
- //Iterate through each of the datasets, and build this into a property of the chart
- helpers.each(data.datasets,function(dataset){
-
- var datasetObject = {
- label : dataset.label || null,
- fillColor : dataset.fillColor,
- strokeColor : dataset.strokeColor,
- pointColor : dataset.pointColor,
- pointStrokeColor : dataset.pointStrokeColor,
- points : []
- };
-
- this.datasets.push(datasetObject);
-
-
- helpers.each(dataset.data,function(dataPoint,index){
- //Add a new point for each piece of data, passing any required data to draw.
- datasetObject.points.push(new this.PointClass({
- value : dataPoint,
- label : data.labels[index],
- datasetLabel: dataset.label,
- strokeColor : dataset.pointStrokeColor,
- fillColor : dataset.pointColor,
- highlightFill : dataset.pointHighlightFill || dataset.pointColor,
- highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
- }));
- },this);
-
- this.buildScale(data.labels);
-
-
- this.eachPoints(function(point, index){
- helpers.extend(point, {
- x: this.scale.calculateX(index),
- y: this.scale.endPoint
- });
- point.save();
- }, this);
-
- },this);
-
-
- this.render();
- },
- update : function(){
- this.scale.update();
- // Reset any highlight colours before updating.
- helpers.each(this.activeElements, function(activeElement){
- activeElement.restore(['fillColor', 'strokeColor']);
- });
- this.eachPoints(function(point){
- point.save();
- });
- this.render();
- },
- eachPoints : function(callback){
- helpers.each(this.datasets,function(dataset){
- helpers.each(dataset.points,callback,this);
- },this);
- },
- getPointsAtEvent : function(e){
- var pointsArray = [],
- eventPosition = helpers.getRelativePosition(e);
- helpers.each(this.datasets,function(dataset){
- helpers.each(dataset.points,function(point){
- if (point.inRange(eventPosition.x,eventPosition.y)) pointsArray.push(point);
- });
- },this);
- return pointsArray;
- },
- buildScale : function(labels){
- var self = this;
-
- var dataTotal = function(){
- var values = [];
- self.eachPoints(function(point){
- values.push(point.value);
- });
-
- return values;
- };
-
- var scaleOptions = {
- templateString : this.options.scaleLabel,
- height : this.chart.height,
- width : this.chart.width,
- ctx : this.chart.ctx,
- textColor : this.options.scaleFontColor,
- fontSize : this.options.scaleFontSize,
- fontStyle : this.options.scaleFontStyle,
- fontFamily : this.options.scaleFontFamily,
- valuesCount : labels.length,
- beginAtZero : this.options.scaleBeginAtZero,
- integersOnly : this.options.scaleIntegersOnly,
- calculateYRange : function(currentHeight){
- var updatedRanges = helpers.calculateScaleRange(
- dataTotal(),
- currentHeight,
- this.fontSize,
- this.beginAtZero,
- this.integersOnly
- );
- helpers.extend(this, updatedRanges);
- },
- xLabels : labels,
- font : helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
- lineWidth : this.options.scaleLineWidth,
- lineColor : this.options.scaleLineColor,
- showHorizontalLines : this.options.scaleShowHorizontalLines,
- showVerticalLines : this.options.scaleShowVerticalLines,
- gridLineWidth : (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
- gridLineColor : (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
- padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
- showLabels : this.options.scaleShowLabels,
- display : this.options.showScale
- };
-
- if (this.options.scaleOverride){
- helpers.extend(scaleOptions, {
- calculateYRange: helpers.noop,
- steps: this.options.scaleSteps,
- stepValue: this.options.scaleStepWidth,
- min: this.options.scaleStartValue,
- max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
- });
- }
-
-
- this.scale = new Chart.Scale(scaleOptions);
- },
- addData : function(valuesArray,label){
- //Map the values array for each of the datasets
-
- helpers.each(valuesArray,function(value,datasetIndex){
- //Add a new point for each piece of data, passing any required data to draw.
- this.datasets[datasetIndex].points.push(new this.PointClass({
- value : value,
- label : label,
- x: this.scale.calculateX(this.scale.valuesCount+1),
- y: this.scale.endPoint,
- strokeColor : this.datasets[datasetIndex].pointStrokeColor,
- fillColor : this.datasets[datasetIndex].pointColor
- }));
- },this);
-
- this.scale.addXLabel(label);
- //Then re-render the chart.
- this.update();
- },
- removeData : function(){
- this.scale.removeXLabel();
- //Then re-render the chart.
- helpers.each(this.datasets,function(dataset){
- dataset.points.shift();
- },this);
- this.update();
- },
- reflow : function(){
- var newScaleProps = helpers.extend({
- height : this.chart.height,
- width : this.chart.width
- });
- this.scale.update(newScaleProps);
- },
- draw : function(ease){
- var easingDecimal = ease || 1;
- this.clear();
-
- var ctx = this.chart.ctx;
-
- // Some helper methods for getting the next/prev points
- var hasValue = function(item){
- return item.value !== null;
- },
- nextPoint = function(point, collection, index){
- return helpers.findNextWhere(collection, hasValue, index) || point;
- },
- previousPoint = function(point, collection, index){
- return helpers.findPreviousWhere(collection, hasValue, index) || point;
- };
-
- this.scale.draw(easingDecimal);
-
-
- helpers.each(this.datasets,function(dataset){
- var pointsWithValues = helpers.where(dataset.points, hasValue);
-
- //Transition each point first so that the line and point drawing isn't out of sync
- //We can use this extra loop to calculate the control points of this dataset also in this loop
-
- helpers.each(dataset.points, function(point, index){
- if (point.hasValue()){
- point.transition({
- y : this.scale.calculateY(point.value),
- x : this.scale.calculateX(index)
- }, easingDecimal);
- }
- },this);
-
-
- // Control points need to be calculated in a seperate loop, because we need to know the current x/y of the point
- // This would cause issues when there is no animation, because the y of the next point would be 0, so beziers would be skewed
- if (this.options.bezierCurve){
- helpers.each(pointsWithValues, function(point, index){
- var tension = (index > 0 && index < pointsWithValues.length - 1) ? this.options.bezierCurveTension : 0;
- point.controlPoints = helpers.splineCurve(
- previousPoint(point, pointsWithValues, index),
- point,
- nextPoint(point, pointsWithValues, index),
- tension
- );
-
- // Prevent the bezier going outside of the bounds of the graph
-
- // Cap puter bezier handles to the upper/lower scale bounds
- if (point.controlPoints.outer.y > this.scale.endPoint){
- point.controlPoints.outer.y = this.scale.endPoint;
- }
- else if (point.controlPoints.outer.y < this.scale.startPoint){
- point.controlPoints.outer.y = this.scale.startPoint;
- }
-
- // Cap inner bezier handles to the upper/lower scale bounds
- if (point.controlPoints.inner.y > this.scale.endPoint){
- point.controlPoints.inner.y = this.scale.endPoint;
- }
- else if (point.controlPoints.inner.y < this.scale.startPoint){
- point.controlPoints.inner.y = this.scale.startPoint;
- }
- },this);
- }
-
-
- //Draw the line between all the points
- ctx.lineWidth = this.options.datasetStrokeWidth;
- ctx.strokeStyle = dataset.strokeColor;
- ctx.beginPath();
-
- helpers.each(pointsWithValues, function(point, index){
- if (index === 0){
- ctx.moveTo(point.x, point.y);
- }
- else{
- if(this.options.bezierCurve){
- var previous = previousPoint(point, pointsWithValues, index);
-
- ctx.bezierCurveTo(
- previous.controlPoints.outer.x,
- previous.controlPoints.outer.y,
- point.controlPoints.inner.x,
- point.controlPoints.inner.y,
- point.x,
- point.y
- );
- }
- else{
- ctx.lineTo(point.x,point.y);
- }
- }
- }, this);
-
- ctx.stroke();
-
- if (this.options.datasetFill && pointsWithValues.length > 0){
- //Round off the line by going to the base of the chart, back to the start, then fill.
- ctx.lineTo(pointsWithValues[pointsWithValues.length - 1].x, this.scale.endPoint);
- ctx.lineTo(pointsWithValues[0].x, this.scale.endPoint);
- ctx.fillStyle = dataset.fillColor;
- ctx.closePath();
- ctx.fill();
- }
-
- //Now draw the points over the line
- //A little inefficient double looping, but better than the line
- //lagging behind the point positions
- helpers.each(pointsWithValues,function(point){
- point.draw();
- });
- },this);
- }
- });
-
-
-}).call(this);
-
-(function(){
- "use strict";
-
- var root = this,
- Chart = root.Chart,
- //Cache a local reference to Chart.helpers
- helpers = Chart.helpers;
-
- var defaultConfig = {
- //Boolean - Show a backdrop to the scale label
- scaleShowLabelBackdrop : true,
-
- //String - The colour of the label backdrop
- scaleBackdropColor : "rgba(255,255,255,0.75)",
-
- // Boolean - Whether the scale should begin at zero
- scaleBeginAtZero : true,
-
- //Number - The backdrop padding above & below the label in pixels
- scaleBackdropPaddingY : 2,
-
- //Number - The backdrop padding to the side of the label in pixels
- scaleBackdropPaddingX : 2,
-
- //Boolean - Show line for each value in the scale
- scaleShowLine : true,
-
- //Boolean - Stroke a line around each segment in the chart
- segmentShowStroke : true,
-
- //String - The colour of the stroke on each segement.
- segmentStrokeColor : "#fff",
-
- //Number - The width of the stroke value in pixels
- segmentStrokeWidth : 2,
-
- //Number - Amount of animation steps
- animationSteps : 100,
-
- //String - Animation easing effect.
- animationEasing : "easeOutBounce",
-
- //Boolean - Whether to animate the rotation of the chart
- animateRotate : true,
-
- //Boolean - Whether to animate scaling the chart from the centre
- animateScale : false,
-
- //String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
- };
-
-
- Chart.Type.extend({
- //Passing in a name registers this chart in the Chart namespace
- name: "PolarArea",
- //Providing a defaults will also register the deafults in the chart namespace
- defaults : defaultConfig,
- //Initialize is fired when the chart is initialized - Data is passed in as a parameter
- //Config is automatically merged by the core of Chart.js, and is available at this.options
- initialize: function(data){
- this.segments = [];
- //Declare segment class as a chart instance specific class, so it can share props for this instance
- this.SegmentArc = Chart.Arc.extend({
- showStroke : this.options.segmentShowStroke,
- strokeWidth : this.options.segmentStrokeWidth,
- strokeColor : this.options.segmentStrokeColor,
- ctx : this.chart.ctx,
- innerRadius : 0,
- x : this.chart.width/2,
- y : this.chart.height/2
- });
- this.scale = new Chart.RadialScale({
- display: this.options.showScale,
- fontStyle: this.options.scaleFontStyle,
- fontSize: this.options.scaleFontSize,
- fontFamily: this.options.scaleFontFamily,
- fontColor: this.options.scaleFontColor,
- showLabels: this.options.scaleShowLabels,
- showLabelBackdrop: this.options.scaleShowLabelBackdrop,
- backdropColor: this.options.scaleBackdropColor,
- backdropPaddingY : this.options.scaleBackdropPaddingY,
- backdropPaddingX: this.options.scaleBackdropPaddingX,
- lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
- lineColor: this.options.scaleLineColor,
- lineArc: true,
- width: this.chart.width,
- height: this.chart.height,
- xCenter: this.chart.width/2,
- yCenter: this.chart.height/2,
- ctx : this.chart.ctx,
- templateString: this.options.scaleLabel,
- valuesCount: data.length
- });
-
- this.updateScaleRange(data);
-
- this.scale.update();
-
- helpers.each(data,function(segment,index){
- this.addData(segment,index,true);
- },this);
-
- //Set up tooltip events on the chart
- if (this.options.showTooltips){
- helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
- var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
- helpers.each(this.segments,function(segment){
- segment.restore(["fillColor"]);
- });
- helpers.each(activeSegments,function(activeSegment){
- activeSegment.fillColor = activeSegment.highlightColor;
- });
- this.showTooltip(activeSegments);
- });
- }
-
- this.render();
- },
- getSegmentsAtEvent : function(e){
- var segmentsArray = [];
-
- var location = helpers.getRelativePosition(e);
-
- helpers.each(this.segments,function(segment){
- if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
- },this);
- return segmentsArray;
- },
- addData : function(segment, atIndex, silent){
- var index = atIndex || this.segments.length;
-
- this.segments.splice(index, 0, new this.SegmentArc({
- fillColor: segment.color,
- highlightColor: segment.highlight || segment.color,
- label: segment.label,
- value: segment.value,
- outerRadius: (this.options.animateScale) ? 0 : this.scale.calculateCenterOffset(segment.value),
- circumference: (this.options.animateRotate) ? 0 : this.scale.getCircumference(),
- startAngle: Math.PI * 1.5
- }));
- if (!silent){
- this.reflow();
- this.update();
- }
- },
- removeData: function(atIndex){
- var indexToDelete = (helpers.isNumber(atIndex)) ? atIndex : this.segments.length-1;
- this.segments.splice(indexToDelete, 1);
- this.reflow();
- this.update();
- },
- calculateTotal: function(data){
- this.total = 0;
- helpers.each(data,function(segment){
- this.total += segment.value;
- },this);
- this.scale.valuesCount = this.segments.length;
- },
- updateScaleRange: function(datapoints){
- var valuesArray = [];
- helpers.each(datapoints,function(segment){
- valuesArray.push(segment.value);
- });
-
- var scaleSizes = (this.options.scaleOverride) ?
- {
- steps: this.options.scaleSteps,
- stepValue: this.options.scaleStepWidth,
- min: this.options.scaleStartValue,
- max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
- } :
- helpers.calculateScaleRange(
- valuesArray,
- helpers.min([this.chart.width, this.chart.height])/2,
- this.options.scaleFontSize,
- this.options.scaleBeginAtZero,
- this.options.scaleIntegersOnly
- );
-
- helpers.extend(
- this.scale,
- scaleSizes,
- {
- size: helpers.min([this.chart.width, this.chart.height]),
- xCenter: this.chart.width/2,
- yCenter: this.chart.height/2
- }
- );
-
- },
- update : function(){
- this.calculateTotal(this.segments);
-
- helpers.each(this.segments,function(segment){
- segment.save();
- });
-
- this.reflow();
- this.render();
- },
- reflow : function(){
- helpers.extend(this.SegmentArc.prototype,{
- x : this.chart.width/2,
- y : this.chart.height/2
- });
- this.updateScaleRange(this.segments);
- this.scale.update();
-
- helpers.extend(this.scale,{
- xCenter: this.chart.width/2,
- yCenter: this.chart.height/2
- });
-
- helpers.each(this.segments, function(segment){
- segment.update({
- outerRadius : this.scale.calculateCenterOffset(segment.value)
- });
- }, this);
-
- },
- draw : function(ease){
- var easingDecimal = ease || 1;
- //Clear & draw the canvas
- this.clear();
- helpers.each(this.segments,function(segment, index){
- segment.transition({
- circumference : this.scale.getCircumference(),
- outerRadius : this.scale.calculateCenterOffset(segment.value)
- },easingDecimal);
-
- segment.endAngle = segment.startAngle + segment.circumference;
-
- // If we've removed the first segment we need to set the first one to
- // start at the top.
- if (index === 0){
- segment.startAngle = Math.PI * 1.5;
- }
-
- //Check to see if it's the last segment, if not get the next and update the start angle
- if (index < this.segments.length - 1){
- this.segments[index+1].startAngle = segment.endAngle;
- }
- segment.draw();
- }, this);
- this.scale.draw();
- }
- });
-
-}).call(this);
-(function(){
- "use strict";
-
- var root = this,
- Chart = root.Chart,
- helpers = Chart.helpers;
-
-
-
- Chart.Type.extend({
- name: "Radar",
- defaults:{
- //Boolean - Whether to show lines for each scale point
- scaleShowLine : true,
-
- //Boolean - Whether we show the angle lines out of the radar
- angleShowLineOut : true,
-
- //Boolean - Whether to show labels on the scale
- scaleShowLabels : false,
-
- // Boolean - Whether the scale should begin at zero
- scaleBeginAtZero : true,
-
- //String - Colour of the angle line
- angleLineColor : "rgba(0,0,0,.1)",
-
- //Number - Pixel width of the angle line
- angleLineWidth : 1,
-
- //String - Point label font declaration
- pointLabelFontFamily : "'Arial'",
-
- //String - Point label font weight
- pointLabelFontStyle : "normal",
-
- //Number - Point label font size in pixels
- pointLabelFontSize : 10,
-
- //String - Point label font colour
- pointLabelFontColor : "#666",
-
- //Boolean - Whether to show a dot for each point
- pointDot : true,
-
- //Number - Radius of each point dot in pixels
- pointDotRadius : 3,
-
- //Number - Pixel width of point dot stroke
- pointDotStrokeWidth : 1,
-
- //Number - amount extra to add to the radius to cater for hit detection outside the drawn point
- pointHitDetectionRadius : 20,
-
- //Boolean - Whether to show a stroke for datasets
- datasetStroke : true,
-
- //Number - Pixel width of dataset stroke
- datasetStrokeWidth : 2,
-
- //Boolean - Whether to fill the dataset with a colour
- datasetFill : true,
-
- //String - A legend template
- legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].strokeColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
-
- },
-
- initialize: function(data){
- this.PointClass = Chart.Point.extend({
- strokeWidth : this.options.pointDotStrokeWidth,
- radius : this.options.pointDotRadius,
- display: this.options.pointDot,
- hitDetectionRadius : this.options.pointHitDetectionRadius,
- ctx : this.chart.ctx
- });
-
- this.datasets = [];
-
- this.buildScale(data);
-
- //Set up tooltip events on the chart
- if (this.options.showTooltips){
- helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
- var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
-
- this.eachPoints(function(point){
- point.restore(['fillColor', 'strokeColor']);
- });
- helpers.each(activePointsCollection, function(activePoint){
- activePoint.fillColor = activePoint.highlightFill;
- activePoint.strokeColor = activePoint.highlightStroke;
- });
-
- this.showTooltip(activePointsCollection);
- });
- }
-
- //Iterate through each of the datasets, and build this into a property of the chart
- helpers.each(data.datasets,function(dataset){
-
- var datasetObject = {
- label: dataset.label || null,
- fillColor : dataset.fillColor,
- strokeColor : dataset.strokeColor,
- pointColor : dataset.pointColor,
- pointStrokeColor : dataset.pointStrokeColor,
- points : []
- };
-
- this.datasets.push(datasetObject);
-
- helpers.each(dataset.data,function(dataPoint,index){
- //Add a new point for each piece of data, passing any required data to draw.
- var pointPosition;
- if (!this.scale.animation){
- pointPosition = this.scale.getPointPosition(index, this.scale.calculateCenterOffset(dataPoint));
- }
- datasetObject.points.push(new this.PointClass({
- value : dataPoint,
- label : data.labels[index],
- datasetLabel: dataset.label,
- x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
- y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
- strokeColor : dataset.pointStrokeColor,
- fillColor : dataset.pointColor,
- highlightFill : dataset.pointHighlightFill || dataset.pointColor,
- highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
- }));
- },this);
-
- },this);
-
- this.render();
- },
- eachPoints : function(callback){
- helpers.each(this.datasets,function(dataset){
- helpers.each(dataset.points,callback,this);
- },this);
- },
-
- getPointsAtEvent : function(evt){
- var mousePosition = helpers.getRelativePosition(evt),
- fromCenter = helpers.getAngleFromPoint({
- x: this.scale.xCenter,
- y: this.scale.yCenter
- }, mousePosition);
-
- var anglePerIndex = (Math.PI * 2) /this.scale.valuesCount,
- pointIndex = Math.round((fromCenter.angle - Math.PI * 1.5) / anglePerIndex),
- activePointsCollection = [];
-
- // If we're at the top, make the pointIndex 0 to get the first of the array.
- if (pointIndex >= this.scale.valuesCount || pointIndex < 0){
- pointIndex = 0;
- }
-
- if (fromCenter.distance <= this.scale.drawingArea){
- helpers.each(this.datasets, function(dataset){
- activePointsCollection.push(dataset.points[pointIndex]);
- });
- }
-
- return activePointsCollection;
- },
-
- buildScale : function(data){
- this.scale = new Chart.RadialScale({
- display: this.options.showScale,
- fontStyle: this.options.scaleFontStyle,
- fontSize: this.options.scaleFontSize,
- fontFamily: this.options.scaleFontFamily,
- fontColor: this.options.scaleFontColor,
- showLabels: this.options.scaleShowLabels,
- showLabelBackdrop: this.options.scaleShowLabelBackdrop,
- backdropColor: this.options.scaleBackdropColor,
- backdropPaddingY : this.options.scaleBackdropPaddingY,
- backdropPaddingX: this.options.scaleBackdropPaddingX,
- lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
- lineColor: this.options.scaleLineColor,
- angleLineColor : this.options.angleLineColor,
- angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,
- // Point labels at the edge of each line
- pointLabelFontColor : this.options.pointLabelFontColor,
- pointLabelFontSize : this.options.pointLabelFontSize,
- pointLabelFontFamily : this.options.pointLabelFontFamily,
- pointLabelFontStyle : this.options.pointLabelFontStyle,
- height : this.chart.height,
- width: this.chart.width,
- xCenter: this.chart.width/2,
- yCenter: this.chart.height/2,
- ctx : this.chart.ctx,
- templateString: this.options.scaleLabel,
- labels: data.labels,
- valuesCount: data.datasets[0].data.length
- });
-
- this.scale.setScaleSize();
- this.updateScaleRange(data.datasets);
- this.scale.buildYLabels();
- },
- updateScaleRange: function(datasets){
- var valuesArray = (function(){
- var totalDataArray = [];
- helpers.each(datasets,function(dataset){
- if (dataset.data){
- totalDataArray = totalDataArray.concat(dataset.data);
- }
- else {
- helpers.each(dataset.points, function(point){
- totalDataArray.push(point.value);
- });
- }
- });
- return totalDataArray;
- })();
-
-
- var scaleSizes = (this.options.scaleOverride) ?
- {
- steps: this.options.scaleSteps,
- stepValue: this.options.scaleStepWidth,
- min: this.options.scaleStartValue,
- max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
- } :
- helpers.calculateScaleRange(
- valuesArray,
- helpers.min([this.chart.width, this.chart.height])/2,
- this.options.scaleFontSize,
- this.options.scaleBeginAtZero,
- this.options.scaleIntegersOnly
- );
-
- helpers.extend(
- this.scale,
- scaleSizes
- );
-
- },
- addData : function(valuesArray,label){
- //Map the values array for each of the datasets
- this.scale.valuesCount++;
- helpers.each(valuesArray,function(value,datasetIndex){
- var pointPosition = this.scale.getPointPosition(this.scale.valuesCount, this.scale.calculateCenterOffset(value));
- this.datasets[datasetIndex].points.push(new this.PointClass({
- value : value,
- label : label,
- x: pointPosition.x,
- y: pointPosition.y,
- strokeColor : this.datasets[datasetIndex].pointStrokeColor,
- fillColor : this.datasets[datasetIndex].pointColor
- }));
- },this);
-
- this.scale.labels.push(label);
-
- this.reflow();
-
- this.update();
- },
- removeData : function(){
- this.scale.valuesCount--;
- this.scale.labels.shift();
- helpers.each(this.datasets,function(dataset){
- dataset.points.shift();
- },this);
- this.reflow();
- this.update();
- },
- update : function(){
- this.eachPoints(function(point){
- point.save();
- });
- this.reflow();
- this.render();
- },
- reflow: function(){
- helpers.extend(this.scale, {
- width : this.chart.width,
- height: this.chart.height,
- size : helpers.min([this.chart.width, this.chart.height]),
- xCenter: this.chart.width/2,
- yCenter: this.chart.height/2
- });
- this.updateScaleRange(this.datasets);
- this.scale.setScaleSize();
- this.scale.buildYLabels();
- },
- draw : function(ease){
- var easeDecimal = ease || 1,
- ctx = this.chart.ctx;
- this.clear();
- this.scale.draw();
-
- helpers.each(this.datasets,function(dataset){
-
- //Transition each point first so that the line and point drawing isn't out of sync
- helpers.each(dataset.points,function(point,index){
- if (point.hasValue()){
- point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
- }
- },this);
-
-
-
- //Draw the line between all the points
- ctx.lineWidth = this.options.datasetStrokeWidth;
- ctx.strokeStyle = dataset.strokeColor;
- ctx.beginPath();
- helpers.each(dataset.points,function(point,index){
- if (index === 0){
- ctx.moveTo(point.x,point.y);
- }
- else{
- ctx.lineTo(point.x,point.y);
- }
- },this);
- ctx.closePath();
- ctx.stroke();
-
- ctx.fillStyle = dataset.fillColor;
- ctx.fill();
-
- //Now draw the points over the line
- //A little inefficient double looping, but better than the line
- //lagging behind the point positions
- helpers.each(dataset.points,function(point){
- if (point.hasValue()){
- point.draw();
- }
- });
-
- },this);
-
- }
-
- });
-
-
-
-
-
-}).call(this); \ No newline at end of file
diff --git a/vendor/assets/javascripts/jasmine-jquery.js b/vendor/assets/javascripts/jasmine-jquery.js
new file mode 100644
index 00000000000..9b7b245009d
--- /dev/null
+++ b/vendor/assets/javascripts/jasmine-jquery.js
@@ -0,0 +1,851 @@
+/* eslint-disable */
+/*
+ Jasmine JQuery 3.0 patched version from this fork : https://github.com/cmrd-senya/jasmine-jquery/blob/jquery3/lib/jasmine-jquery.js
+*/
+/*!
+Jasmine-jQuery: a set of jQuery helpers for Jasmine tests.
+
+Version 2.1.1
+
+https://github.com/velesin/jasmine-jquery
+
+Copyright (c) 2010-2014 Wojciech Zawistowski, Travis Jeffery
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+(function (root, factory) {
+ if (typeof module !== 'undefined' && module.exports) {
+ factory(root, root.jasmine, require('jquery'));
+ } else {
+ factory(root, root.jasmine, root.jQuery);
+ }
+}((function() {return this; })(), function (window, jasmine, $) { "use strict";
+
+ jasmine.spiedEventsKey = function (selector, eventName) {
+ for(var i = 0;; i++) {
+ var key = [eventName, i].toString()
+ if (data.spiedEvents[key] === undefined || data.spiedEvents[key].selector.is($(selector))) {
+ return key
+ }
+ }
+ }
+
+ jasmine.getFixtures = function () {
+ return jasmine.currentFixtures_ = jasmine.currentFixtures_ || new jasmine.Fixtures()
+ }
+
+ jasmine.getStyleFixtures = function () {
+ return jasmine.currentStyleFixtures_ = jasmine.currentStyleFixtures_ || new jasmine.StyleFixtures()
+ }
+
+ jasmine.Fixtures = function () {
+ this.containerId = 'jasmine-fixtures'
+ this.fixturesCache_ = {}
+ this.fixturesPath = 'spec/javascripts/fixtures'
+ }
+
+ jasmine.Fixtures.prototype.set = function (html) {
+ this.cleanUp()
+ return this.createContainer_(html)
+ }
+
+ jasmine.Fixtures.prototype.appendSet= function (html) {
+ this.addToContainer_(html)
+ }
+
+ jasmine.Fixtures.prototype.preload = function () {
+ this.read.apply(this, arguments)
+ }
+
+ jasmine.Fixtures.prototype.load = function () {
+ this.cleanUp()
+ this.createContainer_(this.read.apply(this, arguments))
+ }
+
+ jasmine.Fixtures.prototype.appendLoad = function () {
+ this.addToContainer_(this.read.apply(this, arguments))
+ }
+
+ jasmine.Fixtures.prototype.read = function () {
+ var htmlChunks = []
+ , fixtureUrls = arguments
+
+ for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
+ htmlChunks.push(this.getFixtureHtml_(fixtureUrls[urlIndex]))
+ }
+
+ return htmlChunks.join('')
+ }
+
+ jasmine.Fixtures.prototype.clearCache = function () {
+ this.fixturesCache_ = {}
+ }
+
+ jasmine.Fixtures.prototype.cleanUp = function () {
+ $('#' + this.containerId).remove()
+ }
+
+ jasmine.Fixtures.prototype.sandbox = function (attributes) {
+ var attributesToSet = attributes || {}
+ return $('<div id="sandbox" />').attr(attributesToSet)
+ }
+
+ jasmine.Fixtures.prototype.createContainer_ = function (html) {
+ var container = $('<div>')
+ .attr('id', this.containerId)
+ .html(html)
+
+ $(document.body).append(container)
+ return container
+ }
+
+ jasmine.Fixtures.prototype.addToContainer_ = function (html){
+ var container = $(document.body).find('#'+this.containerId).append(html)
+
+ if (!container.length) {
+ this.createContainer_(html)
+ }
+ }
+
+ jasmine.Fixtures.prototype.getFixtureHtml_ = function (url) {
+ if (typeof this.fixturesCache_[url] === 'undefined') {
+ this.loadFixtureIntoCache_(url)
+ }
+ return this.fixturesCache_[url]
+ }
+
+ jasmine.Fixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) {
+ var self = this
+ , url = this.makeFixtureUrl_(relativeUrl)
+ , htmlText = ''
+ , request = $.ajax({
+ async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
+ cache: false,
+ url: url,
+ dataType: 'html',
+ success: function (data, status, $xhr) {
+ htmlText = $xhr.responseText
+ }
+ }).fail(function ($xhr, status, err) {
+ throw new Error('Fixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')')
+ })
+
+ var scripts = $($.parseHTML(htmlText, true)).find('script[src]') || [];
+
+ scripts.each(function(){
+ $.ajax({
+ async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
+ cache: false,
+ dataType: 'script',
+ url: $(this).attr('src'),
+ success: function (data, status, $xhr) {
+ htmlText += '<script>' + $xhr.responseText + '</script>'
+ },
+ error: function ($xhr, status, err) {
+ throw new Error('Script could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')')
+ }
+ });
+ })
+
+ self.fixturesCache_[relativeUrl] = htmlText;
+ }
+
+ jasmine.Fixtures.prototype.makeFixtureUrl_ = function (relativeUrl){
+ return this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
+ }
+
+ jasmine.Fixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) {
+ return this[methodName].apply(this, passedArguments)
+ }
+
+
+ jasmine.StyleFixtures = function () {
+ this.fixturesCache_ = {}
+ this.fixturesNodes_ = []
+ this.fixturesPath = 'spec/javascripts/fixtures'
+ }
+
+ jasmine.StyleFixtures.prototype.set = function (css) {
+ this.cleanUp()
+ this.createStyle_(css)
+ }
+
+ jasmine.StyleFixtures.prototype.appendSet = function (css) {
+ this.createStyle_(css)
+ }
+
+ jasmine.StyleFixtures.prototype.preload = function () {
+ this.read_.apply(this, arguments)
+ }
+
+ jasmine.StyleFixtures.prototype.load = function () {
+ this.cleanUp()
+ this.createStyle_(this.read_.apply(this, arguments))
+ }
+
+ jasmine.StyleFixtures.prototype.appendLoad = function () {
+ this.createStyle_(this.read_.apply(this, arguments))
+ }
+
+ jasmine.StyleFixtures.prototype.cleanUp = function () {
+ while(this.fixturesNodes_.length) {
+ this.fixturesNodes_.pop().remove()
+ }
+ }
+
+ jasmine.StyleFixtures.prototype.createStyle_ = function (html) {
+ var styleText = $('<div></div>').html(html).text()
+ , style = $('<style>' + styleText + '</style>')
+
+ this.fixturesNodes_.push(style)
+ $('head').append(style)
+ }
+
+ jasmine.StyleFixtures.prototype.clearCache = jasmine.Fixtures.prototype.clearCache
+ jasmine.StyleFixtures.prototype.read_ = jasmine.Fixtures.prototype.read
+ jasmine.StyleFixtures.prototype.getFixtureHtml_ = jasmine.Fixtures.prototype.getFixtureHtml_
+ jasmine.StyleFixtures.prototype.loadFixtureIntoCache_ = jasmine.Fixtures.prototype.loadFixtureIntoCache_
+ jasmine.StyleFixtures.prototype.makeFixtureUrl_ = jasmine.Fixtures.prototype.makeFixtureUrl_
+ jasmine.StyleFixtures.prototype.proxyCallTo_ = jasmine.Fixtures.prototype.proxyCallTo_
+
+ jasmine.getJSONFixtures = function () {
+ return jasmine.currentJSONFixtures_ = jasmine.currentJSONFixtures_ || new jasmine.JSONFixtures()
+ }
+
+ jasmine.JSONFixtures = function () {
+ this.fixturesCache_ = {}
+ this.fixturesPath = 'spec/javascripts/fixtures/json'
+ }
+
+ jasmine.JSONFixtures.prototype.load = function () {
+ this.read.apply(this, arguments)
+ return this.fixturesCache_
+ }
+
+ jasmine.JSONFixtures.prototype.read = function () {
+ var fixtureUrls = arguments
+
+ for(var urlCount = fixtureUrls.length, urlIndex = 0; urlIndex < urlCount; urlIndex++) {
+ this.getFixtureData_(fixtureUrls[urlIndex])
+ }
+
+ return this.fixturesCache_
+ }
+
+ jasmine.JSONFixtures.prototype.clearCache = function () {
+ this.fixturesCache_ = {}
+ }
+
+ jasmine.JSONFixtures.prototype.getFixtureData_ = function (url) {
+ if (!this.fixturesCache_[url]) this.loadFixtureIntoCache_(url)
+ return this.fixturesCache_[url]
+ }
+
+ jasmine.JSONFixtures.prototype.loadFixtureIntoCache_ = function (relativeUrl) {
+ var self = this
+ , url = this.fixturesPath.match('/$') ? this.fixturesPath + relativeUrl : this.fixturesPath + '/' + relativeUrl
+
+ $.ajax({
+ async: false, // must be synchronous to guarantee that no tests are run before fixture is loaded
+ cache: false,
+ dataType: 'json',
+ url: url,
+ success: function (data) {
+ self.fixturesCache_[relativeUrl] = data
+ },
+ error: function ($xhr, status, err) {
+ throw new Error('JSONFixture could not be loaded: ' + url + ' (status: ' + status + ', message: ' + err.message + ')')
+ }
+ })
+ }
+
+ jasmine.JSONFixtures.prototype.proxyCallTo_ = function (methodName, passedArguments) {
+ return this[methodName].apply(this, passedArguments)
+ }
+
+ jasmine.jQuery = function () {}
+
+ jasmine.jQuery.browserTagCaseIndependentHtml = function (html) {
+ return $('<div/>').append(html).html()
+ }
+
+ jasmine.jQuery.elementToString = function (element) {
+ return $(element).map(function () { return this.outerHTML; }).toArray().join(', ')
+ }
+
+ var data = {
+ spiedEvents: {}
+ , handlers: []
+ }
+
+ jasmine.jQuery.events = {
+ spyOn: function (selector, eventName) {
+ var handler = function (e) {
+ var calls = (typeof data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] !== 'undefined') ? data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0
+ data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = {
+ selector: $(selector),
+ args: jasmine.util.argsToArray(arguments),
+ calls: ++calls
+ }
+ }
+
+ $(selector).on(eventName, handler)
+ data.handlers.push(handler)
+
+ return {
+ selector: selector,
+ eventName: eventName,
+ handler: handler,
+ reset: function (){
+ delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
+ },
+ calls: {
+ count: function () {
+ return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ?
+ data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0;
+ },
+ any: function () {
+ return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ?
+ !!data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : false;
+ }
+ }
+ }
+ },
+
+ args: function (selector, eventName) {
+ var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].args
+
+ if (!actualArgs) {
+ throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent."
+ }
+
+ return actualArgs
+ },
+
+ wasTriggered: function (selector, eventName) {
+ return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)])
+ },
+
+ wasTriggeredWith: function (selector, eventName, expectedArgs, util, customEqualityTesters) {
+ var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1)
+
+ if (Object.prototype.toString.call(expectedArgs) !== '[object Array]')
+ actualArgs = actualArgs[0]
+
+ return util.equals(actualArgs, expectedArgs, customEqualityTesters)
+ },
+
+ wasPrevented: function (selector, eventName) {
+ var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
+ , args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args
+ , e = args ? args[0] : undefined
+
+ return e && e.isDefaultPrevented()
+ },
+
+ wasStopped: function (selector, eventName) {
+ var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]
+ , args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args
+ , e = args ? args[0] : undefined
+
+ return e && e.isPropagationStopped()
+ },
+
+ cleanUp: function () {
+ data.spiedEvents = {}
+ data.handlers = []
+ }
+ }
+
+ var hasProperty = function (actualValue, expectedValue) {
+ if (expectedValue === undefined)
+ return actualValue !== undefined
+
+ return actualValue === expectedValue
+ }
+
+ beforeEach(function () {
+ jasmine.addMatchers({
+ toHaveClass: function () {
+ return {
+ compare: function (actual, className) {
+ return { pass: $(actual).hasClass(className) }
+ }
+ }
+ },
+
+ toHaveCss: function () {
+ return {
+ compare: function (actual, css) {
+ var stripCharsRegex = /[\s;\"\']/g
+ for (var prop in css) {
+ var value = css[prop]
+ // see issue #147 on gh
+ ;if ((value === 'auto') && ($(actual).get(0).style[prop] === 'auto')) continue
+ var actualStripped = $(actual).css(prop).replace(stripCharsRegex, '')
+ var valueStripped = value.replace(stripCharsRegex, '')
+ if (actualStripped !== valueStripped) return { pass: false }
+ }
+ return { pass: true }
+ }
+ }
+ },
+
+ toBeVisible: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $(actual).is(':visible') }
+ }
+ }
+ },
+
+ toBeHidden: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $(actual).is(':hidden') }
+ }
+ }
+ },
+
+ toBeSelected: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $(actual).is(':selected') }
+ }
+ }
+ },
+
+ toBeChecked: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $(actual).is(':checked') }
+ }
+ }
+ },
+
+ toBeEmpty: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $(actual).is(':empty') }
+ }
+ }
+ },
+
+ toBeInDOM: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $.contains(document.documentElement, $(actual)[0]) }
+ }
+ }
+ },
+
+ toExist: function () {
+ return {
+ compare: function (actual) {
+ return { pass: $(actual).length }
+ }
+ }
+ },
+
+ toHaveLength: function () {
+ return {
+ compare: function (actual, length) {
+ return { pass: $(actual).length === length }
+ }
+ }
+ },
+
+ toHaveAttr: function () {
+ return {
+ compare: function (actual, attributeName, expectedAttributeValue) {
+ return { pass: hasProperty($(actual).attr(attributeName), expectedAttributeValue) }
+ }
+ }
+ },
+
+ toHaveProp: function () {
+ return {
+ compare: function (actual, propertyName, expectedPropertyValue) {
+ return { pass: hasProperty($(actual).prop(propertyName), expectedPropertyValue) }
+ }
+ }
+ },
+
+ toHaveId: function () {
+ return {
+ compare: function (actual, id) {
+ return { pass: $(actual).attr('id') == id }
+ }
+ }
+ },
+
+ toHaveHtml: function () {
+ return {
+ compare: function (actual, html) {
+ return { pass: $(actual).html() == jasmine.jQuery.browserTagCaseIndependentHtml(html) }
+ }
+ }
+ },
+
+ toContainHtml: function () {
+ return {
+ compare: function (actual, html) {
+ var actualHtml = $(actual).html()
+ , expectedHtml = jasmine.jQuery.browserTagCaseIndependentHtml(html)
+
+ return { pass: (actualHtml.indexOf(expectedHtml) >= 0) }
+ }
+ }
+ },
+
+ toHaveText: function () {
+ return {
+ compare: function (actual, text) {
+ var actualText = $(actual).text()
+ var trimmedText = $.trim(actualText)
+
+ if (text && $.isFunction(text.test)) {
+ return { pass: text.test(actualText) || text.test(trimmedText) }
+ } else {
+ return { pass: (actualText == text || trimmedText == text) }
+ }
+ }
+ }
+ },
+
+ toContainText: function () {
+ return {
+ compare: function (actual, text) {
+ var trimmedText = $.trim($(actual).text())
+
+ if (text && $.isFunction(text.test)) {
+ return { pass: text.test(trimmedText) }
+ } else {
+ return { pass: trimmedText.indexOf(text) != -1 }
+ }
+ }
+ }
+ },
+
+ toHaveValue: function () {
+ return {
+ compare: function (actual, value) {
+ return { pass: $(actual).val() === value }
+ }
+ }
+ },
+
+ toHaveData: function () {
+ return {
+ compare: function (actual, key, expectedValue) {
+ return { pass: hasProperty($(actual).data(key), expectedValue) }
+ }
+ }
+ },
+
+ toContainElement: function () {
+ return {
+ compare: function (actual, selector) {
+ return { pass: $(actual).find(selector).length }
+ }
+ }
+ },
+
+ toBeMatchedBy: function () {
+ return {
+ compare: function (actual, selector) {
+ return { pass: $(actual).filter(selector).length }
+ }
+ }
+ },
+
+ toBeDisabled: function () {
+ return {
+ compare: function (actual, selector) {
+ return { pass: $(actual).is(':disabled') }
+ }
+ }
+ },
+
+ toBeFocused: function (selector) {
+ return {
+ compare: function (actual, selector) {
+ return { pass: $(actual)[0] === $(actual)[0].ownerDocument.activeElement }
+ }
+ }
+ },
+
+ toHandle: function () {
+ return {
+ compare: function (actual, event) {
+ if ( !actual || actual.length === 0 ) return { pass: false };
+ var events = $._data($(actual).get(0), "events")
+
+ if (!events || !event || typeof event !== "string") {
+ return { pass: false }
+ }
+
+ var namespaces = event.split(".")
+ , eventType = namespaces.shift()
+ , sortedNamespaces = namespaces.slice(0).sort()
+ , namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)")
+
+ if (events[eventType] && namespaces.length) {
+ for (var i = 0; i < events[eventType].length; i++) {
+ var namespace = events[eventType][i].namespace
+
+ if (namespaceRegExp.test(namespace))
+ return { pass: true }
+ }
+ } else {
+ return { pass: (events[eventType] && events[eventType].length > 0) }
+ }
+
+ return { pass: false }
+ }
+ }
+ },
+
+ toHandleWith: function () {
+ return {
+ compare: function (actual, eventName, eventHandler) {
+ if ( !actual || actual.length === 0 ) return { pass: false };
+ var normalizedEventName = eventName.split('.')[0]
+ , stack = $._data($(actual).get(0), "events")[normalizedEventName]
+
+ for (var i = 0; i < stack.length; i++) {
+ if (stack[i].handler == eventHandler) return { pass: true }
+ }
+
+ return { pass: false }
+ }
+ }
+ },
+
+ toHaveBeenTriggeredOn: function () {
+ return {
+ compare: function (actual, selector) {
+ var result = { pass: jasmine.jQuery.events.wasTriggered(selector, actual) }
+
+ result.message = result.pass ?
+ "Expected event " + $(actual) + " not to have been triggered on " + selector :
+ "Expected event " + $(actual) + " to have been triggered on " + selector
+
+ return result;
+ }
+ }
+ },
+
+ toHaveBeenTriggered: function (){
+ return {
+ compare: function (actual) {
+ var eventName = actual.eventName
+ , selector = actual.selector
+ , result = { pass: jasmine.jQuery.events.wasTriggered(selector, eventName) }
+
+ result.message = result.pass ?
+ "Expected event " + eventName + " not to have been triggered on " + selector :
+ "Expected event " + eventName + " to have been triggered on " + selector
+
+ return result
+ }
+ }
+ },
+
+ toHaveBeenTriggeredOnAndWith: function (j$, customEqualityTesters) {
+ return {
+ compare: function (actual, selector, expectedArgs) {
+ var wasTriggered = jasmine.jQuery.events.wasTriggered(selector, actual)
+ , result = { pass: wasTriggered && jasmine.jQuery.events.wasTriggeredWith(selector, actual, expectedArgs, j$, customEqualityTesters) }
+
+ if (wasTriggered) {
+ var actualArgs = jasmine.jQuery.events.args(selector, actual, expectedArgs)[1]
+ result.message = result.pass ?
+ "Expected event " + actual + " not to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs) :
+ "Expected event " + actual + " to have been triggered with " + jasmine.pp(expectedArgs) + " but it was triggered with " + jasmine.pp(actualArgs)
+
+ } else {
+ // todo check on this
+ result.message = result.pass ?
+ "Expected event " + actual + " not to have been triggered on " + selector :
+ "Expected event " + actual + " to have been triggered on " + selector
+ }
+
+ return result
+ }
+ }
+ },
+
+ toHaveBeenPreventedOn: function () {
+ return {
+ compare: function (actual, selector) {
+ var result = { pass: jasmine.jQuery.events.wasPrevented(selector, actual) }
+
+ result.message = result.pass ?
+ "Expected event " + actual + " not to have been prevented on " + selector :
+ "Expected event " + actual + " to have been prevented on " + selector
+
+ return result
+ }
+ }
+ },
+
+ toHaveBeenPrevented: function () {
+ return {
+ compare: function (actual) {
+ var eventName = actual.eventName
+ , selector = actual.selector
+ , result = { pass: jasmine.jQuery.events.wasPrevented(selector, eventName) }
+
+ result.message = result.pass ?
+ "Expected event " + eventName + " not to have been prevented on " + selector :
+ "Expected event " + eventName + " to have been prevented on " + selector
+
+ return result
+ }
+ }
+ },
+
+ toHaveBeenStoppedOn: function () {
+ return {
+ compare: function (actual, selector) {
+ var result = { pass: jasmine.jQuery.events.wasStopped(selector, actual) }
+
+ result.message = result.pass ?
+ "Expected event " + actual + " not to have been stopped on " + selector :
+ "Expected event " + actual + " to have been stopped on " + selector
+
+ return result;
+ }
+ }
+ },
+
+ toHaveBeenStopped: function () {
+ return {
+ compare: function (actual) {
+ var eventName = actual.eventName
+ , selector = actual.selector
+ , result = { pass: jasmine.jQuery.events.wasStopped(selector, eventName) }
+
+ result.message = result.pass ?
+ "Expected event " + eventName + " not to have been stopped on " + selector :
+ "Expected event " + eventName + " to have been stopped on " + selector
+
+ return result
+ }
+ }
+ }
+ })
+
+ jasmine.getEnv().addCustomEqualityTester(function(a, b) {
+ if (a && b) {
+ if (a instanceof $ || jasmine.isDomNode(a)) {
+ var $a = $(a)
+
+ if (b instanceof $)
+ return $a.length == b.length && a.is(b)
+
+ return $a.is(b);
+ }
+
+ if (b instanceof $ || jasmine.isDomNode(b)) {
+ var $b = $(b)
+
+ if (a instanceof $)
+ return a.length == $b.length && $b.is(a)
+
+ return $(b).is(a);
+ }
+ }
+ })
+
+ jasmine.getEnv().addCustomEqualityTester(function (a, b) {
+ if (a instanceof $ && b instanceof $ && a.size() == b.size())
+ return a.is(b)
+ })
+ })
+
+ afterEach(function () {
+ jasmine.getFixtures().cleanUp()
+ jasmine.getStyleFixtures().cleanUp()
+ jasmine.jQuery.events.cleanUp()
+ })
+
+ window.readFixtures = function () {
+ return jasmine.getFixtures().proxyCallTo_('read', arguments)
+ }
+
+ window.preloadFixtures = function () {
+ jasmine.getFixtures().proxyCallTo_('preload', arguments)
+ }
+
+ window.loadFixtures = function () {
+ jasmine.getFixtures().proxyCallTo_('load', arguments)
+ }
+
+ window.appendLoadFixtures = function () {
+ jasmine.getFixtures().proxyCallTo_('appendLoad', arguments)
+ }
+
+ window.setFixtures = function (html) {
+ return jasmine.getFixtures().proxyCallTo_('set', arguments)
+ }
+
+ window.appendSetFixtures = function () {
+ jasmine.getFixtures().proxyCallTo_('appendSet', arguments)
+ }
+
+ window.sandbox = function (attributes) {
+ return jasmine.getFixtures().sandbox(attributes)
+ }
+
+ window.spyOnEvent = function (selector, eventName) {
+ return jasmine.jQuery.events.spyOn(selector, eventName)
+ }
+
+ window.preloadStyleFixtures = function () {
+ jasmine.getStyleFixtures().proxyCallTo_('preload', arguments)
+ }
+
+ window.loadStyleFixtures = function () {
+ jasmine.getStyleFixtures().proxyCallTo_('load', arguments)
+ }
+
+ window.appendLoadStyleFixtures = function () {
+ jasmine.getStyleFixtures().proxyCallTo_('appendLoad', arguments)
+ }
+
+ window.setStyleFixtures = function (html) {
+ jasmine.getStyleFixtures().proxyCallTo_('set', arguments)
+ }
+
+ window.appendSetStyleFixtures = function (html) {
+ jasmine.getStyleFixtures().proxyCallTo_('appendSet', arguments)
+ }
+
+ window.loadJSONFixtures = function () {
+ return jasmine.getJSONFixtures().proxyCallTo_('load', arguments)
+ }
+
+ window.getJSONFixture = function (url) {
+ return jasmine.getJSONFixtures().proxyCallTo_('read', arguments)[url]
+ }
+}));
diff --git a/vendor/assets/javascripts/jquery.atwho.js b/vendor/assets/javascripts/jquery.atwho.js
index 0d295ebe5af..e058e13303a 100644
--- a/vendor/assets/javascripts/jquery.atwho.js
+++ b/vendor/assets/javascripts/jquery.atwho.js
@@ -448,7 +448,7 @@ Controller = (function() {
Controller.prototype.insertContentFor = function($li) {
var data, tpl;
tpl = this.getOpt('insertTpl');
- data = $.extend({}, $li.data('item-data'), {
+ data = $.extend({}, $li.data('itemData'), {
'atwho-at': this.at
});
return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
@@ -824,7 +824,7 @@ EditableController = (function(superClass) {
this.$inputor.focus();
}
suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";
- data = $li.data('item-data');
+ 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]);
diff --git a/vendor/assets/javascripts/jquery.endless-scroll.js b/vendor/assets/javascripts/jquery.endless-scroll.js
index 38db6b05101..f022d9a5d06 100644
--- a/vendor/assets/javascripts/jquery.endless-scroll.js
+++ b/vendor/assets/javascripts/jquery.endless-scroll.js
@@ -125,4 +125,4 @@
}
};
-})(jQuery); \ No newline at end of file
+})(jQuery);
diff --git a/vendor/assets/javascripts/jquery.waitforimages.js b/vendor/assets/javascripts/jquery.waitforimages.js
deleted file mode 100644
index 95b39c2e074..00000000000
--- a/vendor/assets/javascripts/jquery.waitforimages.js
+++ /dev/null
@@ -1,144 +0,0 @@
-/*
- * waitForImages 1.4
- * -----------------
- * Provides a callback when all images have loaded in your given selector.
- * http://www.alexanderdickson.com/
- *
- *
- * Copyright (c) 2011 Alex Dickson
- * Licensed under the MIT licenses.
- * See website for more info.
- *
- */
-
-;(function($) {
- // Namespace all events.
- var eventNamespace = 'waitForImages';
-
- // CSS properties which contain references to images.
- $.waitForImages = {
- hasImageProperties: [
- 'backgroundImage',
- 'listStyleImage',
- 'borderImage',
- 'borderCornerImage'
- ]
- };
-
- // Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
- $.expr[':'].uncached = function(obj) {
- // Ensure we are dealing with an `img` element with a valid `src` attribute.
- if ( ! $(obj).is('img[src!=""]')) {
- return false;
- }
-
- // Firefox's `complete` property will always be`true` even if the image has not been downloaded.
- // Doing it this way works in Firefox.
- var img = document.createElement('img');
- img.src = obj.src;
- return ! img.complete;
- };
-
- $.fn.waitForImages = function(finishedCallback, eachCallback, waitForAll) {
-
- // Handle options object.
- if ($.isPlainObject(arguments[0])) {
- eachCallback = finishedCallback.each;
- waitForAll = finishedCallback.waitForAll;
- finishedCallback = finishedCallback.finished;
- }
-
- // Handle missing callbacks.
- finishedCallback = finishedCallback || $.noop;
- eachCallback = eachCallback || $.noop;
-
- // Convert waitForAll to Boolean
- waitForAll = !! waitForAll;
-
- // Ensure callbacks are functions.
- if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
- throw new TypeError('An invalid callback was supplied.');
- };
-
- return this.each(function() {
- // Build a list of all imgs, dependent on what images will be considered.
- var obj = $(this),
- allImgs = [];
-
- if (waitForAll) {
- // CSS properties which may contain an image.
- var hasImgProperties = $.waitForImages.hasImageProperties || [],
- matchUrl = /url\((['"]?)(.*?)\1\)/g;
-
- // Get all elements, as any one of them could have a background image.
- obj.find('*').each(function() {
- var element = $(this);
-
- // If an `img` element, add it. But keep iterating in case it has a background image too.
- if (element.is('img:uncached')) {
- allImgs.push({
- src: element.attr('src'),
- element: element[0]
- });
- }
-
- $.each(hasImgProperties, function(i, property) {
- var propertyValue = element.css(property);
- // If it doesn't contain this property, skip.
- if ( ! propertyValue) {
- return true;
- }
-
- // Get all url() of this element.
- var match;
- while (match = matchUrl.exec(propertyValue)) {
- allImgs.push({
- src: match[2],
- element: element[0]
- });
- };
- });
- });
- } else {
- // For images only, the task is simpler.
- obj
- .find('img:uncached')
- .each(function() {
- allImgs.push({
- src: this.src,
- element: this
- });
- });
- };
-
- var allImgsLength = allImgs.length,
- allImgsLoaded = 0;
-
- // If no images found, don't bother.
- if (allImgsLength == 0) {
- finishedCallback.call(obj[0]);
- };
-
- $.each(allImgs, function(i, img) {
-
- var image = new Image;
-
- // Handle the image loading and error with the same callback.
- $(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function(event) {
- allImgsLoaded++;
-
- // If an error occurred with loading the image, set the third argument accordingly.
- eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
-
- if (allImgsLoaded == allImgsLength) {
- finishedCallback.call(obj[0]);
- return false;
- };
-
- });
-
- image.src = img.src;
- });
- });
- };
-})(jQuery);
diff --git a/vendor/assets/javascripts/katex.js b/vendor/assets/javascripts/katex.js
deleted file mode 100644
index 6b59a3477a7..00000000000
--- a/vendor/assets/javascripts/katex.js
+++ /dev/null
@@ -1,8685 +0,0 @@
-/*
- The MIT License (MIT)
-
- Copyright (c) 2015 Khan Academy
-
- This software also uses portions of the underscore.js project, which is
- MIT licensed with the following copyright:
-
- Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
- Reporters & Editors
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- */
-
-/*
- Here is how to build a version of KaTeX that works with gitlab.
-
- The problem is that the standard procedure for changing font location doesn't work for the empty string.
-
- 1. Clone KaTeX. Anything later than 4fb9445a9 (is merged into master) will do.
- 2. make (requires node)
- 3. sed -e 's,fonts/,,' -e 's/url\(([^)]*)\)/url(font-path\1)/g' build/katex.css > build/katex.scss
- 4. Copy build/katex.js to gitlab/vendor/assets/javascripts/katex.js,
- build/katex.scss to gitlab/vendor/assets/stylesheets/katex.scss and
- fonts/* to gitlab/vendor/assets/fonts/.
-*/
-
-(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.katex = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
-/* eslint no-console:0 */
-/**
- * This is the main entry point for KaTeX. Here, we expose functions for
- * rendering expressions either to DOM nodes or to markup strings.
- *
- * We also expose the ParseError class to check if errors thrown from KaTeX are
- * errors in the expression, or errors in javascript handling.
- */
-
-var ParseError = require("./src/ParseError");
-var Settings = require("./src/Settings");
-
-var buildTree = require("./src/buildTree");
-var parseTree = require("./src/parseTree");
-var utils = require("./src/utils");
-
-/**
- * Parse and build an expression, and place that expression in the DOM node
- * given.
- */
-var render = function(expression, baseNode, options) {
- utils.clearNode(baseNode);
-
- var settings = new Settings(options);
-
- var tree = parseTree(expression, settings);
- var node = buildTree(tree, expression, settings).toNode();
-
- baseNode.appendChild(node);
-};
-
-// KaTeX's styles don't work properly in quirks mode. Print out an error, and
-// disable rendering.
-if (typeof document !== "undefined") {
- if (document.compatMode !== "CSS1Compat") {
- typeof console !== "undefined" && console.warn(
- "Warning: KaTeX doesn't work in quirks mode. Make sure your " +
- "website has a suitable doctype.");
-
- render = function() {
- throw new ParseError("KaTeX doesn't work in quirks mode.");
- };
- }
-}
-
-/**
- * Parse and build an expression, and return the markup for that.
- */
-var renderToString = function(expression, options) {
- var settings = new Settings(options);
-
- var tree = parseTree(expression, settings);
- return buildTree(tree, expression, settings).toMarkup();
-};
-
-/**
- * Parse an expression and return the parse tree.
- */
-var generateParseTree = function(expression, options) {
- var settings = new Settings(options);
- return parseTree(expression, settings);
-};
-
-module.exports = {
- render: render,
- renderToString: renderToString,
- /**
- * NOTE: This method is not currently recommended for public use.
- * The internal tree representation is unstable and is very likely
- * to change. Use at your own risk.
- */
- __parse: generateParseTree,
- ParseError: ParseError,
-};
-
-},{"./src/ParseError":6,"./src/Settings":8,"./src/buildTree":13,"./src/parseTree":22,"./src/utils":25}],2:[function(require,module,exports){
-/** @flow */
-
-"use strict";
-
-function getRelocatable(re) {
- // In the future, this could use a WeakMap instead of an expando.
- if (!re.__matchAtRelocatable) {
- // Disjunctions are the lowest-precedence operator, so we can make any
- // pattern match the empty string by appending `|()` to it:
- // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-patterns
- var source = re.source + "|()";
-
- // We always make the new regex global.
- var flags = "g" + (re.ignoreCase ? "i" : "") + (re.multiline ? "m" : "") + (re.unicode ? "u" : "")
- // sticky (/.../y) doesn't make sense in conjunction with our relocation
- // logic, so we ignore it here.
- ;
-
- re.__matchAtRelocatable = new RegExp(source, flags);
- }
- return re.__matchAtRelocatable;
-}
-
-function matchAt(re, str, pos) {
- if (re.global || re.sticky) {
- throw new Error("matchAt(...): Only non-global regexes are supported");
- }
- var reloc = getRelocatable(re);
- reloc.lastIndex = pos;
- var match = reloc.exec(str);
- // Last capturing group is our sentinel that indicates whether the regex
- // matched at the given location.
- if (match[match.length - 1] == null) {
- // Original regex matched.
- match.length = match.length - 1;
- return match;
- } else {
- return null;
- }
-}
-
-module.exports = matchAt;
-},{}],3:[function(require,module,exports){
-/**
- * The Lexer class handles tokenizing the input in various ways. Since our
- * parser expects us to be able to backtrack, the lexer allows lexing from any
- * given starting point.
- *
- * Its main exposed function is the `lex` function, which takes a position to
- * lex from and a type of token to lex. It defers to the appropriate `_innerLex`
- * function.
- *
- * The various `_innerLex` functions perform the actual lexing of different
- * kinds.
- */
-
-var matchAt = require("match-at");
-
-var ParseError = require("./ParseError");
-
-// The main lexer class
-function Lexer(input) {
- this.input = input;
- this.pos = 0;
-}
-
-/**
- * The resulting token returned from `lex`.
- *
- * It consists of the token text plus some position information.
- * The position information is essentially a range in an input string,
- * but instead of referencing the bare input string, we refer to the lexer.
- * That way it is possible to attach extra metadata to the input string,
- * like for example a file name or similar.
- *
- * The position information (all three parameters) is optional,
- * so it is OK to construct synthetic tokens if appropriate.
- * Not providing available position information may lead to
- * degraded error reporting, though.
- *
- * @param {string} text the text of this token
- * @param {number=} start the start offset, zero-based inclusive
- * @param {number=} end the end offset, zero-based exclusive
- * @param {Lexer=} lexer the lexer which in turn holds the input string
- */
-function Token(text, start, end, lexer) {
- this.text = text;
- this.start = start;
- this.end = end;
- this.lexer = lexer;
-}
-
-/**
- * Given a pair of tokens (this and endToken), compute a “Token†encompassing
- * the whole input range enclosed by these two.
- *
- * @param {Token} endToken last token of the range, inclusive
- * @param {string} text the text of the newly constructed token
- */
-Token.prototype.range = function(endToken, text) {
- if (endToken.lexer !== this.lexer) {
- return new Token(text); // sorry, no position information available
- }
- return new Token(text, this.start, endToken.end, this.lexer);
-};
-
-/* The following tokenRegex
- * - matches typical whitespace (but not NBSP etc.) using its first group
- * - does not match any control character \x00-\x1f except whitespace
- * - does not match a bare backslash
- * - matches any ASCII character except those just mentioned
- * - does not match the BMP private use area \uE000-\uF8FF
- * - does not match bare surrogate code units
- * - matches any BMP character except for those just described
- * - matches any valid Unicode surrogate pair
- * - matches a backslash followed by one or more letters
- * - matches a backslash followed by any BMP character, including newline
- * Just because the Lexer matches something doesn't mean it's valid input:
- * If there is no matching function or symbol definition, the Parser will
- * still reject the input.
- */
-var tokenRegex = new RegExp(
- "([ \r\n\t]+)|" + // whitespace
- "([!-\\[\\]-\u2027\u202A-\uD7FF\uF900-\uFFFF]" + // single codepoint
- "|[\uD800-\uDBFF][\uDC00-\uDFFF]" + // surrogate pair
- "|\\\\(?:[a-zA-Z]+|[^\uD800-\uDFFF])" + // function name
- ")"
-);
-
-/**
- * This function lexes a single token.
- */
-Lexer.prototype.lex = function() {
- var input = this.input;
- var pos = this.pos;
- if (pos === input.length) {
- return new Token("EOF", pos, pos, this);
- }
- var match = matchAt(tokenRegex, input, pos);
- if (match === null) {
- throw new ParseError(
- "Unexpected character: '" + input[pos] + "'",
- new Token(input[pos], pos, pos + 1, this));
- }
- var text = match[2] || " ";
- var start = this.pos;
- this.pos += match[0].length;
- var end = this.pos;
- return new Token(text, start, end, this);
-};
-
-module.exports = Lexer;
-
-},{"./ParseError":6,"match-at":2}],4:[function(require,module,exports){
-/**
- * This file contains the “gullet†where macros are expanded
- * until only non-macro tokens remain.
- */
-
-var Lexer = require("./Lexer");
-
-function MacroExpander(input, macros) {
- this.lexer = new Lexer(input);
- this.macros = macros;
- this.stack = []; // contains tokens in REVERSE order
- this.discardedWhiteSpace = [];
-}
-
-/**
- * Recursively expand first token, then return first non-expandable token.
- */
-MacroExpander.prototype.nextToken = function() {
- for (;;) {
- if (this.stack.length === 0) {
- this.stack.push(this.lexer.lex());
- }
- var topToken = this.stack.pop();
- var name = topToken.text;
- if (!(name.charAt(0) === "\\" && this.macros.hasOwnProperty(name))) {
- return topToken;
- }
- var expansion = this.macros[name];
- if (typeof expansion === "string") {
- var bodyLexer = new Lexer(expansion);
- expansion = [];
- var tok = bodyLexer.lex();
- while (tok.text !== "EOF") {
- expansion.push(tok);
- tok = bodyLexer.lex();
- }
- expansion.reverse(); // to fit in with stack using push and pop
- this.macros[name] = expansion;
- }
- this.stack = this.stack.concat(expansion);
- }
-};
-
-MacroExpander.prototype.get = function(ignoreSpace) {
- this.discardedWhiteSpace = [];
- var token = this.nextToken();
- if (ignoreSpace) {
- while (token.text === " ") {
- this.discardedWhiteSpace.push(token);
- token = this.nextToken();
- }
- }
- return token;
-};
-
-/**
- * Undo the effect of the preceding call to the get method.
- * A call to this method MUST be immediately preceded and immediately followed
- * by a call to get. Only used during mode switching, i.e. after one token
- * was got in the old mode but should get got again in a new mode
- * with possibly different whitespace handling.
- */
-MacroExpander.prototype.unget = function(token) {
- this.stack.push(token);
- while (this.discardedWhiteSpace.length !== 0) {
- this.stack.push(this.discardedWhiteSpace.pop());
- }
-};
-
-module.exports = MacroExpander;
-
-},{"./Lexer":3}],5:[function(require,module,exports){
-/**
- * This file contains information about the options that the Parser carries
- * around with it while parsing. Data is held in an `Options` object, and when
- * recursing, a new `Options` object can be created with the `.with*` and
- * `.reset` functions.
- */
-
-/**
- * This is the main options class. It contains the style, size, color, and font
- * of the current parse level. It also contains the style and size of the parent
- * parse level, so size changes can be handled efficiently.
- *
- * Each of the `.with*` and `.reset` functions passes its current style and size
- * as the parentStyle and parentSize of the new options class, so parent
- * handling is taken care of automatically.
- */
-function Options(data) {
- this.style = data.style;
- this.color = data.color;
- this.size = data.size;
- this.phantom = data.phantom;
- this.font = data.font;
-
- if (data.parentStyle === undefined) {
- this.parentStyle = data.style;
- } else {
- this.parentStyle = data.parentStyle;
- }
-
- if (data.parentSize === undefined) {
- this.parentSize = data.size;
- } else {
- this.parentSize = data.parentSize;
- }
-}
-
-/**
- * Returns a new options object with the same properties as "this". Properties
- * from "extension" will be copied to the new options object.
- */
-Options.prototype.extend = function(extension) {
- var data = {
- style: this.style,
- size: this.size,
- color: this.color,
- parentStyle: this.style,
- parentSize: this.size,
- phantom: this.phantom,
- font: this.font,
- };
-
- for (var key in extension) {
- if (extension.hasOwnProperty(key)) {
- data[key] = extension[key];
- }
- }
-
- return new Options(data);
-};
-
-/**
- * Create a new options object with the given style.
- */
-Options.prototype.withStyle = function(style) {
- return this.extend({
- style: style,
- });
-};
-
-/**
- * Create a new options object with the given size.
- */
-Options.prototype.withSize = function(size) {
- return this.extend({
- size: size,
- });
-};
-
-/**
- * Create a new options object with the given color.
- */
-Options.prototype.withColor = function(color) {
- return this.extend({
- color: color,
- });
-};
-
-/**
- * Create a new options object with "phantom" set to true.
- */
-Options.prototype.withPhantom = function() {
- return this.extend({
- phantom: true,
- });
-};
-
-/**
- * Create a new options objects with the give font.
- */
-Options.prototype.withFont = function(font) {
- return this.extend({
- font: font,
- });
-};
-
-/**
- * Create a new options object with the same style, size, and color. This is
- * used so that parent style and size changes are handled correctly.
- */
-Options.prototype.reset = function() {
- return this.extend({});
-};
-
-/**
- * A map of color names to CSS colors.
- * TODO(emily): Remove this when we have real macros
- */
-var colorMap = {
- "katex-blue": "#6495ed",
- "katex-orange": "#ffa500",
- "katex-pink": "#ff00af",
- "katex-red": "#df0030",
- "katex-green": "#28ae7b",
- "katex-gray": "gray",
- "katex-purple": "#9d38bd",
- "katex-blueA": "#ccfaff",
- "katex-blueB": "#80f6ff",
- "katex-blueC": "#63d9ea",
- "katex-blueD": "#11accd",
- "katex-blueE": "#0c7f99",
- "katex-tealA": "#94fff5",
- "katex-tealB": "#26edd5",
- "katex-tealC": "#01d1c1",
- "katex-tealD": "#01a995",
- "katex-tealE": "#208170",
- "katex-greenA": "#b6ffb0",
- "katex-greenB": "#8af281",
- "katex-greenC": "#74cf70",
- "katex-greenD": "#1fab54",
- "katex-greenE": "#0d923f",
- "katex-goldA": "#ffd0a9",
- "katex-goldB": "#ffbb71",
- "katex-goldC": "#ff9c39",
- "katex-goldD": "#e07d10",
- "katex-goldE": "#a75a05",
- "katex-redA": "#fca9a9",
- "katex-redB": "#ff8482",
- "katex-redC": "#f9685d",
- "katex-redD": "#e84d39",
- "katex-redE": "#bc2612",
- "katex-maroonA": "#ffbde0",
- "katex-maroonB": "#ff92c6",
- "katex-maroonC": "#ed5fa6",
- "katex-maroonD": "#ca337c",
- "katex-maroonE": "#9e034e",
- "katex-purpleA": "#ddd7ff",
- "katex-purpleB": "#c6b9fc",
- "katex-purpleC": "#aa87ff",
- "katex-purpleD": "#7854ab",
- "katex-purpleE": "#543b78",
- "katex-mintA": "#f5f9e8",
- "katex-mintB": "#edf2df",
- "katex-mintC": "#e0e5cc",
- "katex-grayA": "#f6f7f7",
- "katex-grayB": "#f0f1f2",
- "katex-grayC": "#e3e5e6",
- "katex-grayD": "#d6d8da",
- "katex-grayE": "#babec2",
- "katex-grayF": "#888d93",
- "katex-grayG": "#626569",
- "katex-grayH": "#3b3e40",
- "katex-grayI": "#21242c",
- "katex-kaBlue": "#314453",
- "katex-kaGreen": "#71B307",
-};
-
-/**
- * Gets the CSS color of the current options object, accounting for the
- * `colorMap`.
- */
-Options.prototype.getColor = function() {
- if (this.phantom) {
- return "transparent";
- } else {
- return colorMap[this.color] || this.color;
- }
-};
-
-module.exports = Options;
-
-},{}],6:[function(require,module,exports){
-/**
- * This is the ParseError class, which is the main error thrown by KaTeX
- * functions when something has gone wrong. This is used to distinguish internal
- * errors from errors in the expression that the user provided.
- *
- * If possible, a caller should provide a Token or ParseNode with information
- * about where in the source string the problem occurred.
- *
- * @param {string} message The error message
- * @param {(Token|ParseNode)=} token An object providing position information
- */
-function ParseError(message, token) {
- var error = "KaTeX parse error: " + message;
- var start;
- var end;
-
- if (token && token.lexer && token.start <= token.end) {
- // If we have the input and a position, make the error a bit fancier
-
- // Get the input
- var input = token.lexer.input;
-
- // Prepend some information
- start = token.start;
- end = token.end;
- if (start === input.length) {
- error += " at end of input: ";
- } else {
- error += " at position " + (start + 1) + ": ";
- }
-
- // Underline token in question using combining underscores
- var underlined = input.slice(start, end).replace(/[^]/g, "$&\u0332");
-
- // Extract some context from the input and add it to the error
- var left;
- if (start > 15) {
- left = "…" + input.slice(start - 15, start);
- } else {
- left = input.slice(0, start);
- }
- var right;
- if (end + 15 < input.length) {
- right = input.slice(end, end + 15) + "…";
- } else {
- right = input.slice(end);
- }
- error += left + underlined + right;
- }
-
- // Some hackery to make ParseError a prototype of Error
- // See http://stackoverflow.com/a/8460753
- var self = new Error(error);
- self.name = "ParseError";
- self.__proto__ = ParseError.prototype;
-
- self.position = start;
- return self;
-}
-
-// More hackery
-ParseError.prototype.__proto__ = Error.prototype;
-
-module.exports = ParseError;
-
-},{}],7:[function(require,module,exports){
-/* eslint no-constant-condition:0 */
-var functions = require("./functions");
-var environments = require("./environments");
-var MacroExpander = require("./MacroExpander");
-var symbols = require("./symbols");
-var utils = require("./utils");
-var cjkRegex = require("./unicodeRegexes").cjkRegex;
-
-var parseData = require("./parseData");
-var ParseError = require("./ParseError");
-
-/**
- * This file contains the parser used to parse out a TeX expression from the
- * input. Since TeX isn't context-free, standard parsers don't work particularly
- * well.
- *
- * The strategy of this parser is as such:
- *
- * The main functions (the `.parse...` ones) take a position in the current
- * parse string to parse tokens from. The lexer (found in Lexer.js, stored at
- * this.lexer) also supports pulling out tokens at arbitrary places. When
- * individual tokens are needed at a position, the lexer is called to pull out a
- * token, which is then used.
- *
- * The parser has a property called "mode" indicating the mode that
- * the parser is currently in. Currently it has to be one of "math" or
- * "text", which denotes whether the current environment is a math-y
- * one or a text-y one (e.g. inside \text). Currently, this serves to
- * limit the functions which can be used in text mode.
- *
- * The main functions then return an object which contains the useful data that
- * was parsed at its given point, and a new position at the end of the parsed
- * data. The main functions can call each other and continue the parsing by
- * using the returned position as a new starting point.
- *
- * There are also extra `.handle...` functions, which pull out some reused
- * functionality into self-contained functions.
- *
- * The earlier functions return ParseNodes.
- * The later functions (which are called deeper in the parse) sometimes return
- * ParseFuncOrArgument, which contain a ParseNode as well as some data about
- * whether the parsed object is a function which is missing some arguments, or a
- * standalone object which can be used as an argument to another function.
- */
-
-/**
- * Main Parser class
- */
-function Parser(input, settings) {
- // Create a new macro expander (gullet) and (indirectly via that) also a
- // new lexer (mouth) for this parser (stomach, in the language of TeX)
- this.gullet = new MacroExpander(input, settings.macros);
- // Store the settings for use in parsing
- this.settings = settings;
-}
-
-var ParseNode = parseData.ParseNode;
-
-/**
- * An initial function (without its arguments), or an argument to a function.
- * The `result` argument should be a ParseNode.
- */
-function ParseFuncOrArgument(result, isFunction, token) {
- this.result = result;
- // Is this a function (i.e. is it something defined in functions.js)?
- this.isFunction = isFunction;
- this.token = token;
-}
-
-/**
- * Checks a result to make sure it has the right type, and throws an
- * appropriate error otherwise.
- *
- * @param {boolean=} consume whether to consume the expected token,
- * defaults to true
- */
-Parser.prototype.expect = function(text, consume) {
- if (this.nextToken.text !== text) {
- throw new ParseError(
- "Expected '" + text + "', got '" + this.nextToken.text + "'",
- this.nextToken
- );
- }
- if (consume !== false) {
- this.consume();
- }
-};
-
-/**
- * Considers the current look ahead token as consumed,
- * and fetches the one after that as the new look ahead.
- */
-Parser.prototype.consume = function() {
- this.nextToken = this.gullet.get(this.mode === "math");
-};
-
-Parser.prototype.switchMode = function(newMode) {
- this.gullet.unget(this.nextToken);
- this.mode = newMode;
- this.consume();
-};
-
-/**
- * Main parsing function, which parses an entire input.
- *
- * @return {?Array.<ParseNode>}
- */
-Parser.prototype.parse = function() {
- // Try to parse the input
- this.mode = "math";
- this.consume();
- var parse = this.parseInput();
- return parse;
-};
-
-/**
- * Parses an entire input tree.
- */
-Parser.prototype.parseInput = function() {
- // Parse an expression
- var expression = this.parseExpression(false);
- // If we succeeded, make sure there's an EOF at the end
- this.expect("EOF", false);
- return expression;
-};
-
-var endOfExpression = ["}", "\\end", "\\right", "&", "\\\\", "\\cr"];
-
-/**
- * Parses an "expression", which is a list of atoms.
- *
- * @param {boolean} breakOnInfix Should the parsing stop when we hit infix
- * nodes? This happens when functions have higher precendence
- * than infix nodes in implicit parses.
- *
- * @param {?string} breakOnTokenText The text of the token that the expression
- * should end with, or `null` if something else should end the
- * expression.
- *
- * @return {ParseNode}
- */
-Parser.prototype.parseExpression = function(breakOnInfix, breakOnTokenText) {
- var body = [];
- // Keep adding atoms to the body until we can't parse any more atoms (either
- // we reached the end, a }, or a \right)
- while (true) {
- var lex = this.nextToken;
- if (endOfExpression.indexOf(lex.text) !== -1) {
- break;
- }
- if (breakOnTokenText && lex.text === breakOnTokenText) {
- break;
- }
- if (breakOnInfix && functions[lex.text] && functions[lex.text].infix) {
- break;
- }
- var atom = this.parseAtom();
- if (!atom) {
- if (!this.settings.throwOnError && lex.text[0] === "\\") {
- var errorNode = this.handleUnsupportedCmd();
- body.push(errorNode);
- continue;
- }
-
- break;
- }
- body.push(atom);
- }
- return this.handleInfixNodes(body);
-};
-
-/**
- * Rewrites infix operators such as \over with corresponding commands such
- * as \frac.
- *
- * There can only be one infix operator per group. If there's more than one
- * then the expression is ambiguous. This can be resolved by adding {}.
- *
- * @returns {Array}
- */
-Parser.prototype.handleInfixNodes = function(body) {
- var overIndex = -1;
- var funcName;
-
- for (var i = 0; i < body.length; i++) {
- var node = body[i];
- if (node.type === "infix") {
- if (overIndex !== -1) {
- throw new ParseError(
- "only one infix operator per group",
- node.value.token);
- }
- overIndex = i;
- funcName = node.value.replaceWith;
- }
- }
-
- if (overIndex !== -1) {
- var numerNode;
- var denomNode;
-
- var numerBody = body.slice(0, overIndex);
- var denomBody = body.slice(overIndex + 1);
-
- if (numerBody.length === 1 && numerBody[0].type === "ordgroup") {
- numerNode = numerBody[0];
- } else {
- numerNode = new ParseNode("ordgroup", numerBody, this.mode);
- }
-
- if (denomBody.length === 1 && denomBody[0].type === "ordgroup") {
- denomNode = denomBody[0];
- } else {
- denomNode = new ParseNode("ordgroup", denomBody, this.mode);
- }
-
- var value = this.callFunction(
- funcName, [numerNode, denomNode], null);
- return [new ParseNode(value.type, value, this.mode)];
- } else {
- return body;
- }
-};
-
-// The greediness of a superscript or subscript
-var SUPSUB_GREEDINESS = 1;
-
-/**
- * Handle a subscript or superscript with nice errors.
- */
-Parser.prototype.handleSupSubscript = function(name) {
- var symbolToken = this.nextToken;
- var symbol = symbolToken.text;
- this.consume();
- var group = this.parseGroup();
-
- if (!group) {
- if (!this.settings.throwOnError && this.nextToken.text[0] === "\\") {
- return this.handleUnsupportedCmd();
- } else {
- throw new ParseError(
- "Expected group after '" + symbol + "'",
- symbolToken
- );
- }
- } else if (group.isFunction) {
- // ^ and _ have a greediness, so handle interactions with functions'
- // greediness
- var funcGreediness = functions[group.result].greediness;
- if (funcGreediness > SUPSUB_GREEDINESS) {
- return this.parseFunction(group);
- } else {
- throw new ParseError(
- "Got function '" + group.result + "' with no arguments " +
- "as " + name, symbolToken);
- }
- } else {
- return group.result;
- }
-};
-
-/**
- * Converts the textual input of an unsupported command into a text node
- * contained within a color node whose color is determined by errorColor
- */
-Parser.prototype.handleUnsupportedCmd = function() {
- var text = this.nextToken.text;
- var textordArray = [];
-
- for (var i = 0; i < text.length; i++) {
- textordArray.push(new ParseNode("textord", text[i], "text"));
- }
-
- var textNode = new ParseNode(
- "text",
- {
- body: textordArray,
- type: "text",
- },
- this.mode);
-
- var colorNode = new ParseNode(
- "color",
- {
- color: this.settings.errorColor,
- value: [textNode],
- type: "color",
- },
- this.mode);
-
- this.consume();
- return colorNode;
-};
-
-/**
- * Parses a group with optional super/subscripts.
- *
- * @return {?ParseNode}
- */
-Parser.prototype.parseAtom = function() {
- // The body of an atom is an implicit group, so that things like
- // \left(x\right)^2 work correctly.
- var base = this.parseImplicitGroup();
-
- // In text mode, we don't have superscripts or subscripts
- if (this.mode === "text") {
- return base;
- }
-
- // Note that base may be empty (i.e. null) at this point.
-
- var superscript;
- var subscript;
- while (true) {
- // Lex the first token
- var lex = this.nextToken;
-
- if (lex.text === "\\limits" || lex.text === "\\nolimits") {
- // We got a limit control
- if (!base || base.type !== "op") {
- throw new ParseError(
- "Limit controls must follow a math operator",
- lex);
- } else {
- var limits = lex.text === "\\limits";
- base.value.limits = limits;
- base.value.alwaysHandleSupSub = true;
- }
- this.consume();
- } else if (lex.text === "^") {
- // We got a superscript start
- if (superscript) {
- throw new ParseError("Double superscript", lex);
- }
- superscript = this.handleSupSubscript("superscript");
- } else if (lex.text === "_") {
- // We got a subscript start
- if (subscript) {
- throw new ParseError("Double subscript", lex);
- }
- subscript = this.handleSupSubscript("subscript");
- } else if (lex.text === "'") {
- // We got a prime
- var prime = new ParseNode("textord", "\\prime", this.mode);
-
- // Many primes can be grouped together, so we handle this here
- var primes = [prime];
- this.consume();
- // Keep lexing tokens until we get something that's not a prime
- while (this.nextToken.text === "'") {
- // For each one, add another prime to the list
- primes.push(prime);
- this.consume();
- }
- // Put them into an ordgroup as the superscript
- superscript = new ParseNode("ordgroup", primes, this.mode);
- } else {
- // If it wasn't ^, _, or ', stop parsing super/subscripts
- break;
- }
- }
-
- if (superscript || subscript) {
- // If we got either a superscript or subscript, create a supsub
- return new ParseNode("supsub", {
- base: base,
- sup: superscript,
- sub: subscript,
- }, this.mode);
- } else {
- // Otherwise return the original body
- return base;
- }
-};
-
-// A list of the size-changing functions, for use in parseImplicitGroup
-var sizeFuncs = [
- "\\tiny", "\\scriptsize", "\\footnotesize", "\\small", "\\normalsize",
- "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
-];
-
-// A list of the style-changing functions, for use in parseImplicitGroup
-var styleFuncs = [
- "\\displaystyle", "\\textstyle", "\\scriptstyle", "\\scriptscriptstyle",
-];
-
-/**
- * Parses an implicit group, which is a group that starts at the end of a
- * specified, and ends right before a higher explicit group ends, or at EOL. It
- * is used for functions that appear to affect the current style, like \Large or
- * \textrm, where instead of keeping a style we just pretend that there is an
- * implicit grouping after it until the end of the group. E.g.
- * small text {\Large large text} small text again
- * It is also used for \left and \right to get the correct grouping.
- *
- * @return {?ParseNode}
- */
-Parser.prototype.parseImplicitGroup = function() {
- var start = this.parseSymbol();
-
- if (start == null) {
- // If we didn't get anything we handle, fall back to parseFunction
- return this.parseFunction();
- }
-
- var func = start.result;
- var body;
-
- if (func === "\\left") {
- // If we see a left:
- // Parse the entire left function (including the delimiter)
- var left = this.parseFunction(start);
- // Parse out the implicit body
- body = this.parseExpression(false);
- // Check the next token
- this.expect("\\right", false);
- var right = this.parseFunction();
- return new ParseNode("leftright", {
- body: body,
- left: left.value.value,
- right: right.value.value,
- }, this.mode);
- } else if (func === "\\begin") {
- // begin...end is similar to left...right
- var begin = this.parseFunction(start);
- var envName = begin.value.name;
- if (!environments.hasOwnProperty(envName)) {
- throw new ParseError(
- "No such environment: " + envName, begin.value.nameGroup);
- }
- // Build the environment object. Arguments and other information will
- // be made available to the begin and end methods using properties.
- var env = environments[envName];
- var args = this.parseArguments("\\begin{" + envName + "}", env);
- var context = {
- mode: this.mode,
- envName: envName,
- parser: this,
- positions: args.pop(),
- };
- var result = env.handler(context, args);
- this.expect("\\end", false);
- var endNameToken = this.nextToken;
- var end = this.parseFunction();
- if (end.value.name !== envName) {
- throw new ParseError(
- "Mismatch: \\begin{" + envName + "} matched " +
- "by \\end{" + end.value.name + "}",
- endNameToken);
- }
- result.position = end.position;
- return result;
- } else if (utils.contains(sizeFuncs, func)) {
- // If we see a sizing function, parse out the implict body
- body = this.parseExpression(false);
- return new ParseNode("sizing", {
- // Figure out what size to use based on the list of functions above
- size: "size" + (utils.indexOf(sizeFuncs, func) + 1),
- value: body,
- }, this.mode);
- } else if (utils.contains(styleFuncs, func)) {
- // If we see a styling function, parse out the implict body
- body = this.parseExpression(true);
- return new ParseNode("styling", {
- // Figure out what style to use by pulling out the style from
- // the function name
- style: func.slice(1, func.length - 5),
- value: body,
- }, this.mode);
- } else {
- // Defer to parseFunction if it's not a function we handle
- return this.parseFunction(start);
- }
-};
-
-/**
- * Parses an entire function, including its base and all of its arguments.
- * The base might either have been parsed already, in which case
- * it is provided as an argument, or it's the next group in the input.
- *
- * @param {ParseFuncOrArgument=} baseGroup optional as described above
- * @return {?ParseNode}
- */
-Parser.prototype.parseFunction = function(baseGroup) {
- if (!baseGroup) {
- baseGroup = this.parseGroup();
- }
-
- if (baseGroup) {
- if (baseGroup.isFunction) {
- var func = baseGroup.result;
- var funcData = functions[func];
- if (this.mode === "text" && !funcData.allowedInText) {
- throw new ParseError(
- "Can't use function '" + func + "' in text mode",
- baseGroup.token);
- }
-
- var args = this.parseArguments(func, funcData);
- var token = baseGroup.token;
- var result = this.callFunction(func, args, args.pop(), token);
- return new ParseNode(result.type, result, this.mode);
- } else {
- return baseGroup.result;
- }
- } else {
- return null;
- }
-};
-
-/**
- * Call a function handler with a suitable context and arguments.
- */
-Parser.prototype.callFunction = function(name, args, positions, token) {
- var context = {
- funcName: name,
- parser: this,
- positions: positions,
- token: token,
- };
- return functions[name].handler(context, args);
-};
-
-/**
- * Parses the arguments of a function or environment
- *
- * @param {string} func "\name" or "\begin{name}"
- * @param {{numArgs:number,numOptionalArgs:number|undefined}} funcData
- * @return the array of arguments, with the list of positions as last element
- */
-Parser.prototype.parseArguments = function(func, funcData) {
- var totalArgs = funcData.numArgs + funcData.numOptionalArgs;
- if (totalArgs === 0) {
- return [[this.pos]];
- }
-
- var baseGreediness = funcData.greediness;
- var positions = [this.pos];
- var args = [];
-
- for (var i = 0; i < totalArgs; i++) {
- var nextToken = this.nextToken;
- var argType = funcData.argTypes && funcData.argTypes[i];
- var arg;
- if (i < funcData.numOptionalArgs) {
- if (argType) {
- arg = this.parseGroupOfType(argType, true);
- } else {
- arg = this.parseGroup(true);
- }
- if (!arg) {
- args.push(null);
- positions.push(this.pos);
- continue;
- }
- } else {
- if (argType) {
- arg = this.parseGroupOfType(argType);
- } else {
- arg = this.parseGroup();
- }
- if (!arg) {
- if (!this.settings.throwOnError &&
- this.nextToken.text[0] === "\\") {
- arg = new ParseFuncOrArgument(
- this.handleUnsupportedCmd(this.nextToken.text),
- false);
- } else {
- throw new ParseError(
- "Expected group after '" + func + "'", nextToken);
- }
- }
- }
- var argNode;
- if (arg.isFunction) {
- var argGreediness =
- functions[arg.result].greediness;
- if (argGreediness > baseGreediness) {
- argNode = this.parseFunction(arg);
- } else {
- throw new ParseError(
- "Got function '" + arg.result + "' as " +
- "argument to '" + func + "'", nextToken);
- }
- } else {
- argNode = arg.result;
- }
- args.push(argNode);
- positions.push(this.pos);
- }
-
- args.push(positions);
-
- return args;
-};
-
-
-/**
- * Parses a group when the mode is changing.
- *
- * @return {?ParseFuncOrArgument}
- */
-Parser.prototype.parseGroupOfType = function(innerMode, optional) {
- var outerMode = this.mode;
- // Handle `original` argTypes
- if (innerMode === "original") {
- innerMode = outerMode;
- }
-
- if (innerMode === "color") {
- return this.parseColorGroup(optional);
- }
- if (innerMode === "size") {
- return this.parseSizeGroup(optional);
- }
-
- this.switchMode(innerMode);
- if (innerMode === "text") {
- // text mode is special because it should ignore the whitespace before
- // it
- while (this.nextToken.text === " ") {
- this.consume();
- }
- }
- // By the time we get here, innerMode is one of "text" or "math".
- // We switch the mode of the parser, recurse, then restore the old mode.
- var res = this.parseGroup(optional);
- this.switchMode(outerMode);
- return res;
-};
-
-/**
- * Parses a group, essentially returning the string formed by the
- * brace-enclosed tokens plus some position information.
- *
- * @param {string} modeName Used to describe the mode in error messages
- * @param {boolean=} optional Whether the group is optional or required
- */
-Parser.prototype.parseStringGroup = function(modeName, optional) {
- if (optional && this.nextToken.text !== "[") {
- return null;
- }
- var outerMode = this.mode;
- this.mode = "text";
- this.expect(optional ? "[" : "{");
- var str = "";
- var firstToken = this.nextToken;
- var lastToken = firstToken;
- while (this.nextToken.text !== (optional ? "]" : "}")) {
- if (this.nextToken.text === "EOF") {
- throw new ParseError(
- "Unexpected end of input in " + modeName,
- firstToken.range(this.nextToken, str));
- }
- lastToken = this.nextToken;
- str += lastToken.text;
- this.consume();
- }
- this.mode = outerMode;
- this.expect(optional ? "]" : "}");
- return firstToken.range(lastToken, str);
-};
-
-/**
- * Parses a color description.
- */
-Parser.prototype.parseColorGroup = function(optional) {
- var res = this.parseStringGroup("color", optional);
- if (!res) {
- return null;
- }
- var match = (/^(#[a-z0-9]+|[a-z]+)$/i).exec(res.text);
- if (!match) {
- throw new ParseError("Invalid color: '" + res.text + "'", res);
- }
- return new ParseFuncOrArgument(
- new ParseNode("color", match[0], this.mode),
- false);
-};
-
-/**
- * Parses a size specification, consisting of magnitude and unit.
- */
-Parser.prototype.parseSizeGroup = function(optional) {
- var res = this.parseStringGroup("size", optional);
- if (!res) {
- return null;
- }
- var match = (/(-?) *(\d+(?:\.\d*)?|\.\d+) *([a-z]{2})/).exec(res.text);
- if (!match) {
- throw new ParseError("Invalid size: '" + res.text + "'", res);
- }
- var data = {
- number: +(match[1] + match[2]), // sign + magnitude, cast to number
- unit: match[3],
- };
- if (data.unit !== "em" && data.unit !== "ex") {
- throw new ParseError("Invalid unit: '" + data.unit + "'", res);
- }
- return new ParseFuncOrArgument(
- new ParseNode("color", data, this.mode),
- false);
-};
-
-/**
- * If the argument is false or absent, this parses an ordinary group,
- * which is either a single nucleus (like "x") or an expression
- * in braces (like "{x+y}").
- * If the argument is true, it parses either a bracket-delimited expression
- * (like "[x+y]") or returns null to indicate the absence of a
- * bracket-enclosed group.
- *
- * @param {boolean=} optional Whether the group is optional or required
- * @return {?ParseFuncOrArgument}
- */
-Parser.prototype.parseGroup = function(optional) {
- var firstToken = this.nextToken;
- // Try to parse an open brace
- if (this.nextToken.text === (optional ? "[" : "{")) {
- // If we get a brace, parse an expression
- this.consume();
- var expression = this.parseExpression(false, optional ? "]" : null);
- var lastToken = this.nextToken;
- // Make sure we get a close brace
- this.expect(optional ? "]" : "}");
- if (this.mode === "text") {
- this.formLigatures(expression);
- }
- return new ParseFuncOrArgument(
- new ParseNode("ordgroup", expression, this.mode,
- firstToken, lastToken),
- false);
- } else {
- // Otherwise, just return a nucleus, or nothing for an optional group
- return optional ? null : this.parseSymbol();
- }
-};
-
-/**
- * Form ligature-like combinations of characters for text mode.
- * This includes inputs like "--", "---", "``" and "''".
- * The result will simply replace multiple textord nodes with a single
- * character in each value by a single textord node having multiple
- * characters in its value. The representation is still ASCII source.
- *
- * @param {Array.<ParseNode>} group the nodes of this group,
- * list will be moified in place
- */
-Parser.prototype.formLigatures = function(group) {
- var i;
- var n = group.length - 1;
- for (i = 0; i < n; ++i) {
- var a = group[i];
- var v = a.value;
- if (v === "-" && group[i + 1].value === "-") {
- if (i + 1 < n && group[i + 2].value === "-") {
- group.splice(i, 3, new ParseNode(
- "textord", "---", "text", a, group[i + 2]));
- n -= 2;
- } else {
- group.splice(i, 2, new ParseNode(
- "textord", "--", "text", a, group[i + 1]));
- n -= 1;
- }
- }
- if ((v === "'" || v === "`") && group[i + 1].value === v) {
- group.splice(i, 2, new ParseNode(
- "textord", v + v, "text", a, group[i + 1]));
- n -= 1;
- }
- }
-};
-
-/**
- * Parse a single symbol out of the string. Here, we handle both the functions
- * we have defined, as well as the single character symbols
- *
- * @return {?ParseFuncOrArgument}
- */
-Parser.prototype.parseSymbol = function() {
- var nucleus = this.nextToken;
-
- if (functions[nucleus.text]) {
- this.consume();
- // If there exists a function with this name, we return the function and
- // say that it is a function.
- return new ParseFuncOrArgument(
- nucleus.text,
- true, nucleus);
- } else if (symbols[this.mode][nucleus.text]) {
- this.consume();
- // Otherwise if this is a no-argument function, find the type it
- // corresponds to in the symbols map
- return new ParseFuncOrArgument(
- new ParseNode(symbols[this.mode][nucleus.text].group,
- nucleus.text, this.mode, nucleus),
- false, nucleus);
- } else if (this.mode === "text" && cjkRegex.test(nucleus.text)) {
- this.consume();
- return new ParseFuncOrArgument(
- new ParseNode("textord", nucleus.text, this.mode, nucleus),
- false, nucleus);
- } else {
- return null;
- }
-};
-
-Parser.prototype.ParseNode = ParseNode;
-
-module.exports = Parser;
-
-},{"./MacroExpander":4,"./ParseError":6,"./environments":16,"./functions":19,"./parseData":21,"./symbols":23,"./unicodeRegexes":24,"./utils":25}],8:[function(require,module,exports){
-/**
- * This is a module for storing settings passed into KaTeX. It correctly handles
- * default settings.
- */
-
-/**
- * Helper function for getting a default value if the value is undefined
- */
-function get(option, defaultValue) {
- return option === undefined ? defaultValue : option;
-}
-
-/**
- * The main Settings object
- *
- * The current options stored are:
- * - displayMode: Whether the expression should be typeset by default in
- * textstyle or displaystyle (default false)
- */
-function Settings(options) {
- // allow null options
- options = options || {};
- this.displayMode = get(options.displayMode, false);
- this.throwOnError = get(options.throwOnError, true);
- this.errorColor = get(options.errorColor, "#cc0000");
- this.macros = options.macros || {};
-}
-
-module.exports = Settings;
-
-},{}],9:[function(require,module,exports){
-/**
- * This file contains information and classes for the various kinds of styles
- * used in TeX. It provides a generic `Style` class, which holds information
- * about a specific style. It then provides instances of all the different kinds
- * of styles possible, and provides functions to move between them and get
- * information about them.
- */
-
-/**
- * The main style class. Contains a unique id for the style, a size (which is
- * the same for cramped and uncramped version of a style), a cramped flag, and a
- * size multiplier, which gives the size difference between a style and
- * textstyle.
- */
-function Style(id, size, multiplier, cramped) {
- this.id = id;
- this.size = size;
- this.cramped = cramped;
- this.sizeMultiplier = multiplier;
-}
-
-/**
- * Get the style of a superscript given a base in the current style.
- */
-Style.prototype.sup = function() {
- return styles[sup[this.id]];
-};
-
-/**
- * Get the style of a subscript given a base in the current style.
- */
-Style.prototype.sub = function() {
- return styles[sub[this.id]];
-};
-
-/**
- * Get the style of a fraction numerator given the fraction in the current
- * style.
- */
-Style.prototype.fracNum = function() {
- return styles[fracNum[this.id]];
-};
-
-/**
- * Get the style of a fraction denominator given the fraction in the current
- * style.
- */
-Style.prototype.fracDen = function() {
- return styles[fracDen[this.id]];
-};
-
-/**
- * Get the cramped version of a style (in particular, cramping a cramped style
- * doesn't change the style).
- */
-Style.prototype.cramp = function() {
- return styles[cramp[this.id]];
-};
-
-/**
- * HTML class name, like "displaystyle cramped"
- */
-Style.prototype.cls = function() {
- return sizeNames[this.size] + (this.cramped ? " cramped" : " uncramped");
-};
-
-/**
- * HTML Reset class name, like "reset-textstyle"
- */
-Style.prototype.reset = function() {
- return resetNames[this.size];
-};
-
-// IDs of the different styles
-var D = 0;
-var Dc = 1;
-var T = 2;
-var Tc = 3;
-var S = 4;
-var Sc = 5;
-var SS = 6;
-var SSc = 7;
-
-// String names for the different sizes
-var sizeNames = [
- "displaystyle textstyle",
- "textstyle",
- "scriptstyle",
- "scriptscriptstyle",
-];
-
-// Reset names for the different sizes
-var resetNames = [
- "reset-textstyle",
- "reset-textstyle",
- "reset-scriptstyle",
- "reset-scriptscriptstyle",
-];
-
-// Instances of the different styles
-var styles = [
- new Style(D, 0, 1.0, false),
- new Style(Dc, 0, 1.0, true),
- new Style(T, 1, 1.0, false),
- new Style(Tc, 1, 1.0, true),
- new Style(S, 2, 0.7, false),
- new Style(Sc, 2, 0.7, true),
- new Style(SS, 3, 0.5, false),
- new Style(SSc, 3, 0.5, true),
-];
-
-// Lookup tables for switching from one style to another
-var sup = [S, Sc, S, Sc, SS, SSc, SS, SSc];
-var sub = [Sc, Sc, Sc, Sc, SSc, SSc, SSc, SSc];
-var fracNum = [T, Tc, S, Sc, SS, SSc, SS, SSc];
-var fracDen = [Tc, Tc, Sc, Sc, SSc, SSc, SSc, SSc];
-var cramp = [Dc, Dc, Tc, Tc, Sc, Sc, SSc, SSc];
-
-// We only export some of the styles. Also, we don't export the `Style` class so
-// no more styles can be generated.
-module.exports = {
- DISPLAY: styles[D],
- TEXT: styles[T],
- SCRIPT: styles[S],
- SCRIPTSCRIPT: styles[SS],
-};
-
-},{}],10:[function(require,module,exports){
-/* eslint no-console:0 */
-/**
- * This module contains general functions that can be used for building
- * different kinds of domTree nodes in a consistent manner.
- */
-
-var domTree = require("./domTree");
-var fontMetrics = require("./fontMetrics");
-var symbols = require("./symbols");
-var utils = require("./utils");
-
-var greekCapitals = [
- "\\Gamma",
- "\\Delta",
- "\\Theta",
- "\\Lambda",
- "\\Xi",
- "\\Pi",
- "\\Sigma",
- "\\Upsilon",
- "\\Phi",
- "\\Psi",
- "\\Omega",
-];
-
-// The following have to be loaded from Main-Italic font, using class mainit
-var mainitLetters = [
- "\u0131", // dotless i, \imath
- "\u0237", // dotless j, \jmath
- "\u00a3", // \pounds
-];
-
-/**
- * Makes a symbolNode after translation via the list of symbols in symbols.js.
- * Correctly pulls out metrics for the character, and optionally takes a list of
- * classes to be attached to the node.
- */
-var makeSymbol = function(value, style, mode, color, classes) {
- // Replace the value with its replaced value from symbol.js
- if (symbols[mode][value] && symbols[mode][value].replace) {
- value = symbols[mode][value].replace;
- }
-
- var metrics = fontMetrics.getCharacterMetrics(value, style);
-
- var symbolNode;
- if (metrics) {
- symbolNode = new domTree.symbolNode(
- value, metrics.height, metrics.depth, metrics.italic, metrics.skew,
- classes);
- } else {
- // TODO(emily): Figure out a good way to only print this in development
- typeof console !== "undefined" && console.warn(
- "No character metrics for '" + value + "' in style '" +
- style + "'");
- symbolNode = new domTree.symbolNode(value, 0, 0, 0, 0, classes);
- }
-
- if (color) {
- symbolNode.style.color = color;
- }
-
- return symbolNode;
-};
-
-/**
- * Makes a symbol in Main-Regular or AMS-Regular.
- * Used for rel, bin, open, close, inner, and punct.
- */
-var mathsym = function(value, mode, color, classes) {
- // Decide what font to render the symbol in by its entry in the symbols
- // table.
- // Have a special case for when the value = \ because the \ is used as a
- // textord in unsupported command errors but cannot be parsed as a regular
- // text ordinal and is therefore not present as a symbol in the symbols
- // table for text
- if (value === "\\" || symbols[mode][value].font === "main") {
- return makeSymbol(value, "Main-Regular", mode, color, classes);
- } else {
- return makeSymbol(
- value, "AMS-Regular", mode, color, classes.concat(["amsrm"]));
- }
-};
-
-/**
- * Makes a symbol in the default font for mathords and textords.
- */
-var mathDefault = function(value, mode, color, classes, type) {
- if (type === "mathord") {
- return mathit(value, mode, color, classes);
- } else if (type === "textord") {
- return makeSymbol(
- value, "Main-Regular", mode, color, classes.concat(["mathrm"]));
- } else {
- throw new Error("unexpected type: " + type + " in mathDefault");
- }
-};
-
-/**
- * Makes a symbol in the italic math font.
- */
-var mathit = function(value, mode, color, classes) {
- if (/[0-9]/.test(value.charAt(0)) ||
- // glyphs for \imath and \jmath do not exist in Math-Italic so we
- // need to use Main-Italic instead
- utils.contains(mainitLetters, value) ||
- utils.contains(greekCapitals, value)) {
- return makeSymbol(
- value, "Main-Italic", mode, color, classes.concat(["mainit"]));
- } else {
- return makeSymbol(
- value, "Math-Italic", mode, color, classes.concat(["mathit"]));
- }
-};
-
-/**
- * Makes either a mathord or textord in the correct font and color.
- */
-var makeOrd = function(group, options, type) {
- var mode = group.mode;
- var value = group.value;
- if (symbols[mode][value] && symbols[mode][value].replace) {
- value = symbols[mode][value].replace;
- }
-
- var classes = ["mord"];
- var color = options.getColor();
-
- var font = options.font;
- if (font) {
- if (font === "mathit" || utils.contains(mainitLetters, value)) {
- return mathit(value, mode, color, classes);
- } else {
- var fontName = fontMap[font].fontName;
- if (fontMetrics.getCharacterMetrics(value, fontName)) {
- return makeSymbol(
- value, fontName, mode, color, classes.concat([font]));
- } else {
- return mathDefault(value, mode, color, classes, type);
- }
- }
- } else {
- return mathDefault(value, mode, color, classes, type);
- }
-};
-
-/**
- * Calculate the height, depth, and maxFontSize of an element based on its
- * children.
- */
-var sizeElementFromChildren = function(elem) {
- var height = 0;
- var depth = 0;
- var maxFontSize = 0;
-
- if (elem.children) {
- for (var i = 0; i < elem.children.length; i++) {
- if (elem.children[i].height > height) {
- height = elem.children[i].height;
- }
- if (elem.children[i].depth > depth) {
- depth = elem.children[i].depth;
- }
- if (elem.children[i].maxFontSize > maxFontSize) {
- maxFontSize = elem.children[i].maxFontSize;
- }
- }
- }
-
- elem.height = height;
- elem.depth = depth;
- elem.maxFontSize = maxFontSize;
-};
-
-/**
- * Makes a span with the given list of classes, list of children, and color.
- */
-var makeSpan = function(classes, children, color) {
- var span = new domTree.span(classes, children);
-
- sizeElementFromChildren(span);
-
- if (color) {
- span.style.color = color;
- }
-
- return span;
-};
-
-/**
- * Makes a document fragment with the given list of children.
- */
-var makeFragment = function(children) {
- var fragment = new domTree.documentFragment(children);
-
- sizeElementFromChildren(fragment);
-
- return fragment;
-};
-
-/**
- * Makes an element placed in each of the vlist elements to ensure that each
- * element has the same max font size. To do this, we create a zero-width space
- * with the correct font size.
- */
-var makeFontSizer = function(options, fontSize) {
- var fontSizeInner = makeSpan([], [new domTree.symbolNode("\u200b")]);
- fontSizeInner.style.fontSize =
- (fontSize / options.style.sizeMultiplier) + "em";
-
- var fontSizer = makeSpan(
- ["fontsize-ensurer", "reset-" + options.size, "size5"],
- [fontSizeInner]);
-
- return fontSizer;
-};
-
-/**
- * Makes a vertical list by stacking elements and kerns on top of each other.
- * Allows for many different ways of specifying the positioning method.
- *
- * Arguments:
- * - children: A list of child or kern nodes to be stacked on top of each other
- * (i.e. the first element will be at the bottom, and the last at
- * the top). Element nodes are specified as
- * {type: "elem", elem: node}
- * while kern nodes are specified as
- * {type: "kern", size: size}
- * - positionType: The method by which the vlist should be positioned. Valid
- * values are:
- * - "individualShift": The children list only contains elem
- * nodes, and each node contains an extra
- * "shift" value of how much it should be
- * shifted (note that shifting is always
- * moving downwards). positionData is
- * ignored.
- * - "top": The positionData specifies the topmost point of
- * the vlist (note this is expected to be a height,
- * so positive values move up)
- * - "bottom": The positionData specifies the bottommost point
- * of the vlist (note this is expected to be a
- * depth, so positive values move down
- * - "shift": The vlist will be positioned such that its
- * baseline is positionData away from the baseline
- * of the first child. Positive values move
- * downwards.
- * - "firstBaseline": The vlist will be positioned such that
- * its baseline is aligned with the
- * baseline of the first child.
- * positionData is ignored. (this is
- * equivalent to "shift" with
- * positionData=0)
- * - positionData: Data used in different ways depending on positionType
- * - options: An Options object
- *
- */
-var makeVList = function(children, positionType, positionData, options) {
- var depth;
- var currPos;
- var i;
- if (positionType === "individualShift") {
- var oldChildren = children;
- children = [oldChildren[0]];
-
- // Add in kerns to the list of children to get each element to be
- // shifted to the correct specified shift
- depth = -oldChildren[0].shift - oldChildren[0].elem.depth;
- currPos = depth;
- for (i = 1; i < oldChildren.length; i++) {
- var diff = -oldChildren[i].shift - currPos -
- oldChildren[i].elem.depth;
- var size = diff -
- (oldChildren[i - 1].elem.height +
- oldChildren[i - 1].elem.depth);
-
- currPos = currPos + diff;
-
- children.push({type: "kern", size: size});
- children.push(oldChildren[i]);
- }
- } else if (positionType === "top") {
- // We always start at the bottom, so calculate the bottom by adding up
- // all the sizes
- var bottom = positionData;
- for (i = 0; i < children.length; i++) {
- if (children[i].type === "kern") {
- bottom -= children[i].size;
- } else {
- bottom -= children[i].elem.height + children[i].elem.depth;
- }
- }
- depth = bottom;
- } else if (positionType === "bottom") {
- depth = -positionData;
- } else if (positionType === "shift") {
- depth = -children[0].elem.depth - positionData;
- } else if (positionType === "firstBaseline") {
- depth = -children[0].elem.depth;
- } else {
- depth = 0;
- }
-
- // Make the fontSizer
- var maxFontSize = 0;
- for (i = 0; i < children.length; i++) {
- if (children[i].type === "elem") {
- maxFontSize = Math.max(maxFontSize, children[i].elem.maxFontSize);
- }
- }
- var fontSizer = makeFontSizer(options, maxFontSize);
-
- // Create a new list of actual children at the correct offsets
- var realChildren = [];
- currPos = depth;
- for (i = 0; i < children.length; i++) {
- if (children[i].type === "kern") {
- currPos += children[i].size;
- } else {
- var child = children[i].elem;
-
- var shift = -child.depth - currPos;
- currPos += child.height + child.depth;
-
- var childWrap = makeSpan([], [fontSizer, child]);
- childWrap.height -= shift;
- childWrap.depth += shift;
- childWrap.style.top = shift + "em";
-
- realChildren.push(childWrap);
- }
- }
-
- // Add in an element at the end with no offset to fix the calculation of
- // baselines in some browsers (namely IE, sometimes safari)
- var baselineFix = makeSpan(
- ["baseline-fix"], [fontSizer, new domTree.symbolNode("\u200b")]);
- realChildren.push(baselineFix);
-
- var vlist = makeSpan(["vlist"], realChildren);
- // Fix the final height and depth, in case there were kerns at the ends
- // since the makeSpan calculation won't take that in to account.
- vlist.height = Math.max(currPos, vlist.height);
- vlist.depth = Math.max(-depth, vlist.depth);
- return vlist;
-};
-
-// A table of size -> font size for the different sizing functions
-var sizingMultiplier = {
- size1: 0.5,
- size2: 0.7,
- size3: 0.8,
- size4: 0.9,
- size5: 1.0,
- size6: 1.2,
- size7: 1.44,
- size8: 1.73,
- size9: 2.07,
- size10: 2.49,
-};
-
-// A map of spacing functions to their attributes, like size and corresponding
-// CSS class
-var spacingFunctions = {
- "\\qquad": {
- size: "2em",
- className: "qquad",
- },
- "\\quad": {
- size: "1em",
- className: "quad",
- },
- "\\enspace": {
- size: "0.5em",
- className: "enspace",
- },
- "\\;": {
- size: "0.277778em",
- className: "thickspace",
- },
- "\\:": {
- size: "0.22222em",
- className: "mediumspace",
- },
- "\\,": {
- size: "0.16667em",
- className: "thinspace",
- },
- "\\!": {
- size: "-0.16667em",
- className: "negativethinspace",
- },
-};
-
-/**
- * Maps TeX font commands to objects containing:
- * - variant: string used for "mathvariant" attribute in buildMathML.js
- * - fontName: the "style" parameter to fontMetrics.getCharacterMetrics
- */
-// A map between tex font commands an MathML mathvariant attribute values
-var fontMap = {
- // styles
- "mathbf": {
- variant: "bold",
- fontName: "Main-Bold",
- },
- "mathrm": {
- variant: "normal",
- fontName: "Main-Regular",
- },
-
- // "mathit" is missing because it requires the use of two fonts: Main-Italic
- // and Math-Italic. This is handled by a special case in makeOrd which ends
- // up calling mathit.
-
- // families
- "mathbb": {
- variant: "double-struck",
- fontName: "AMS-Regular",
- },
- "mathcal": {
- variant: "script",
- fontName: "Caligraphic-Regular",
- },
- "mathfrak": {
- variant: "fraktur",
- fontName: "Fraktur-Regular",
- },
- "mathscr": {
- variant: "script",
- fontName: "Script-Regular",
- },
- "mathsf": {
- variant: "sans-serif",
- fontName: "SansSerif-Regular",
- },
- "mathtt": {
- variant: "monospace",
- fontName: "Typewriter-Regular",
- },
-};
-
-module.exports = {
- fontMap: fontMap,
- makeSymbol: makeSymbol,
- mathsym: mathsym,
- makeSpan: makeSpan,
- makeFragment: makeFragment,
- makeVList: makeVList,
- makeOrd: makeOrd,
- sizingMultiplier: sizingMultiplier,
- spacingFunctions: spacingFunctions,
-};
-
-},{"./domTree":15,"./fontMetrics":17,"./symbols":23,"./utils":25}],11:[function(require,module,exports){
-/* eslint no-console:0 */
-/**
- * This file does the main work of building a domTree structure from a parse
- * tree. The entry point is the `buildHTML` function, which takes a parse tree.
- * Then, the buildExpression, buildGroup, and various groupTypes functions are
- * called, to produce a final HTML tree.
- */
-
-var ParseError = require("./ParseError");
-var Style = require("./Style");
-
-var buildCommon = require("./buildCommon");
-var delimiter = require("./delimiter");
-var domTree = require("./domTree");
-var fontMetrics = require("./fontMetrics");
-var utils = require("./utils");
-
-var makeSpan = buildCommon.makeSpan;
-
-/**
- * Take a list of nodes, build them in order, and return a list of the built
- * nodes. This function handles the `prev` node correctly, and passes the
- * previous element from the list as the prev of the next element.
- */
-var buildExpression = function(expression, options, prev) {
- var groups = [];
- for (var i = 0; i < expression.length; i++) {
- var group = expression[i];
- groups.push(buildGroup(group, options, prev));
- prev = group;
- }
- return groups;
-};
-
-// List of types used by getTypeOfGroup,
-// see https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
-var groupToType = {
- mathord: "mord",
- textord: "mord",
- bin: "mbin",
- rel: "mrel",
- text: "mord",
- open: "mopen",
- close: "mclose",
- inner: "minner",
- genfrac: "mord",
- array: "mord",
- spacing: "mord",
- punct: "mpunct",
- ordgroup: "mord",
- op: "mop",
- katex: "mord",
- overline: "mord",
- underline: "mord",
- rule: "mord",
- leftright: "minner",
- sqrt: "mord",
- accent: "mord",
-};
-
-/**
- * Gets the final math type of an expression, given its group type. This type is
- * used to determine spacing between elements, and affects bin elements by
- * causing them to change depending on what types are around them. This type
- * must be attached to the outermost node of an element as a CSS class so that
- * spacing with its surrounding elements works correctly.
- *
- * Some elements can be mapped one-to-one from group type to math type, and
- * those are listed in the `groupToType` table.
- *
- * Others (usually elements that wrap around other elements) often have
- * recursive definitions, and thus call `getTypeOfGroup` on their inner
- * elements.
- */
-var getTypeOfGroup = function(group) {
- if (group == null) {
- // Like when typesetting $^3$
- return groupToType.mathord;
- } else if (group.type === "supsub") {
- return getTypeOfGroup(group.value.base);
- } else if (group.type === "llap" || group.type === "rlap") {
- return getTypeOfGroup(group.value);
- } else if (group.type === "color") {
- return getTypeOfGroup(group.value.value);
- } else if (group.type === "sizing") {
- return getTypeOfGroup(group.value.value);
- } else if (group.type === "styling") {
- return getTypeOfGroup(group.value.value);
- } else if (group.type === "delimsizing") {
- return groupToType[group.value.delimType];
- } else {
- return groupToType[group.type];
- }
-};
-
-/**
- * Sometimes, groups perform special rules when they have superscripts or
- * subscripts attached to them. This function lets the `supsub` group know that
- * its inner element should handle the superscripts and subscripts instead of
- * handling them itself.
- */
-var shouldHandleSupSub = function(group, options) {
- if (!group) {
- return false;
- } else if (group.type === "op") {
- // Operators handle supsubs differently when they have limits
- // (e.g. `\displaystyle\sum_2^3`)
- return group.value.limits &&
- (options.style.size === Style.DISPLAY.size ||
- group.value.alwaysHandleSupSub);
- } else if (group.type === "accent") {
- return isCharacterBox(group.value.base);
- } else {
- return null;
- }
-};
-
-/**
- * Sometimes we want to pull out the innermost element of a group. In most
- * cases, this will just be the group itself, but when ordgroups and colors have
- * a single element, we want to pull that out.
- */
-var getBaseElem = function(group) {
- if (!group) {
- return false;
- } else if (group.type === "ordgroup") {
- if (group.value.length === 1) {
- return getBaseElem(group.value[0]);
- } else {
- return group;
- }
- } else if (group.type === "color") {
- if (group.value.value.length === 1) {
- return getBaseElem(group.value.value[0]);
- } else {
- return group;
- }
- } else if (group.type === "font") {
- return getBaseElem(group.value.body);
- } else {
- return group;
- }
-};
-
-/**
- * TeXbook algorithms often reference "character boxes", which are simply groups
- * with a single character in them. To decide if something is a character box,
- * we find its innermost group, and see if it is a single character.
- */
-var isCharacterBox = function(group) {
- var baseElem = getBaseElem(group);
-
- // These are all they types of groups which hold single characters
- return baseElem.type === "mathord" ||
- baseElem.type === "textord" ||
- baseElem.type === "bin" ||
- baseElem.type === "rel" ||
- baseElem.type === "inner" ||
- baseElem.type === "open" ||
- baseElem.type === "close" ||
- baseElem.type === "punct";
-};
-
-var makeNullDelimiter = function(options) {
- return makeSpan([
- "sizing", "reset-" + options.size, "size5",
- options.style.reset(), Style.TEXT.cls(),
- "nulldelimiter",
- ]);
-};
-
-/**
- * This is a map of group types to the function used to handle that type.
- * Simpler types come at the beginning, while complicated types come afterwards.
- */
-var groupTypes = {};
-
-groupTypes.mathord = function(group, options, prev) {
- return buildCommon.makeOrd(group, options, "mathord");
-};
-
-groupTypes.textord = function(group, options, prev) {
- return buildCommon.makeOrd(group, options, "textord");
-};
-
-groupTypes.bin = function(group, options, prev) {
- var className = "mbin";
- // Pull out the most recent element. Do some special handling to find
- // things at the end of a \color group. Note that we don't use the same
- // logic for ordgroups (which count as ords).
- var prevAtom = prev;
- while (prevAtom && prevAtom.type === "color") {
- var atoms = prevAtom.value.value;
- prevAtom = atoms[atoms.length - 1];
- }
- // See TeXbook pg. 442-446, Rules 5 and 6, and the text before Rule 19.
- // Here, we determine whether the bin should turn into an ord. We
- // currently only apply Rule 5.
- if (!prev || utils.contains(["mbin", "mopen", "mrel", "mop", "mpunct"],
- getTypeOfGroup(prevAtom))) {
- group.type = "textord";
- className = "mord";
- }
-
- return buildCommon.mathsym(
- group.value, group.mode, options.getColor(), [className]);
-};
-
-groupTypes.rel = function(group, options, prev) {
- return buildCommon.mathsym(
- group.value, group.mode, options.getColor(), ["mrel"]);
-};
-
-groupTypes.open = function(group, options, prev) {
- return buildCommon.mathsym(
- group.value, group.mode, options.getColor(), ["mopen"]);
-};
-
-groupTypes.close = function(group, options, prev) {
- return buildCommon.mathsym(
- group.value, group.mode, options.getColor(), ["mclose"]);
-};
-
-groupTypes.inner = function(group, options, prev) {
- return buildCommon.mathsym(
- group.value, group.mode, options.getColor(), ["minner"]);
-};
-
-groupTypes.punct = function(group, options, prev) {
- return buildCommon.mathsym(
- group.value, group.mode, options.getColor(), ["mpunct"]);
-};
-
-groupTypes.ordgroup = function(group, options, prev) {
- return makeSpan(
- ["mord", options.style.cls()],
- buildExpression(group.value, options.reset())
- );
-};
-
-groupTypes.text = function(group, options, prev) {
- return makeSpan(["text", "mord", options.style.cls()],
- buildExpression(group.value.body, options.reset()));
-};
-
-groupTypes.color = function(group, options, prev) {
- var elements = buildExpression(
- group.value.value,
- options.withColor(group.value.color),
- prev
- );
-
- // \color isn't supposed to affect the type of the elements it contains.
- // To accomplish this, we wrap the results in a fragment, so the inner
- // elements will be able to directly interact with their neighbors. For
- // example, `\color{red}{2 +} 3` has the same spacing as `2 + 3`
- return new buildCommon.makeFragment(elements);
-};
-
-groupTypes.supsub = function(group, options, prev) {
- // Superscript and subscripts are handled in the TeXbook on page
- // 445-446, rules 18(a-f).
-
- // Here is where we defer to the inner group if it should handle
- // superscripts and subscripts itself.
- if (shouldHandleSupSub(group.value.base, options)) {
- return groupTypes[group.value.base.type](group, options, prev);
- }
-
- var base = buildGroup(group.value.base, options.reset());
- var supmid;
- var submid;
- var sup;
- var sub;
-
- if (group.value.sup) {
- sup = buildGroup(group.value.sup,
- options.withStyle(options.style.sup()));
- supmid = makeSpan(
- [options.style.reset(), options.style.sup().cls()], [sup]);
- }
-
- if (group.value.sub) {
- sub = buildGroup(group.value.sub,
- options.withStyle(options.style.sub()));
- submid = makeSpan(
- [options.style.reset(), options.style.sub().cls()], [sub]);
- }
-
- // Rule 18a
- var supShift;
- var subShift;
- if (isCharacterBox(group.value.base)) {
- supShift = 0;
- subShift = 0;
- } else {
- supShift = base.height - fontMetrics.metrics.supDrop;
- subShift = base.depth + fontMetrics.metrics.subDrop;
- }
-
- // Rule 18c
- var minSupShift;
- if (options.style === Style.DISPLAY) {
- minSupShift = fontMetrics.metrics.sup1;
- } else if (options.style.cramped) {
- minSupShift = fontMetrics.metrics.sup3;
- } else {
- minSupShift = fontMetrics.metrics.sup2;
- }
-
- // scriptspace is a font-size-independent size, so scale it
- // appropriately
- var multiplier = Style.TEXT.sizeMultiplier *
- options.style.sizeMultiplier;
- var scriptspace =
- (0.5 / fontMetrics.metrics.ptPerEm) / multiplier + "em";
-
- var supsub;
- if (!group.value.sup) {
- // Rule 18b
- subShift = Math.max(
- subShift, fontMetrics.metrics.sub1,
- sub.height - 0.8 * fontMetrics.metrics.xHeight);
-
- supsub = buildCommon.makeVList([
- {type: "elem", elem: submid},
- ], "shift", subShift, options);
-
- supsub.children[0].style.marginRight = scriptspace;
-
- // Subscripts shouldn't be shifted by the base's italic correction.
- // Account for that by shifting the subscript back the appropriate
- // amount. Note we only do this when the base is a single symbol.
- if (base instanceof domTree.symbolNode) {
- supsub.children[0].style.marginLeft = -base.italic + "em";
- }
- } else if (!group.value.sub) {
- // Rule 18c, d
- supShift = Math.max(supShift, minSupShift,
- sup.depth + 0.25 * fontMetrics.metrics.xHeight);
-
- supsub = buildCommon.makeVList([
- {type: "elem", elem: supmid},
- ], "shift", -supShift, options);
-
- supsub.children[0].style.marginRight = scriptspace;
- } else {
- supShift = Math.max(
- supShift, minSupShift,
- sup.depth + 0.25 * fontMetrics.metrics.xHeight);
- subShift = Math.max(subShift, fontMetrics.metrics.sub2);
-
- var ruleWidth = fontMetrics.metrics.defaultRuleThickness;
-
- // Rule 18e
- if ((supShift - sup.depth) - (sub.height - subShift) <
- 4 * ruleWidth) {
- subShift = 4 * ruleWidth - (supShift - sup.depth) + sub.height;
- var psi = 0.8 * fontMetrics.metrics.xHeight -
- (supShift - sup.depth);
- if (psi > 0) {
- supShift += psi;
- subShift -= psi;
- }
- }
-
- supsub = buildCommon.makeVList([
- {type: "elem", elem: submid, shift: subShift},
- {type: "elem", elem: supmid, shift: -supShift},
- ], "individualShift", null, options);
-
- // See comment above about subscripts not being shifted
- if (base instanceof domTree.symbolNode) {
- supsub.children[0].style.marginLeft = -base.italic + "em";
- }
-
- supsub.children[0].style.marginRight = scriptspace;
- supsub.children[1].style.marginRight = scriptspace;
- }
-
- // We ensure to wrap the supsub vlist in a span.msupsub to reset text-align
- return makeSpan([getTypeOfGroup(group.value.base)],
- [base, makeSpan(["msupsub"], [supsub])]);
-};
-
-groupTypes.genfrac = function(group, options, prev) {
- // Fractions are handled in the TeXbook on pages 444-445, rules 15(a-e).
- // Figure out what style this fraction should be in based on the
- // function used
- var fstyle = options.style;
- if (group.value.size === "display") {
- fstyle = Style.DISPLAY;
- } else if (group.value.size === "text") {
- fstyle = Style.TEXT;
- }
-
- var nstyle = fstyle.fracNum();
- var dstyle = fstyle.fracDen();
-
- var numer = buildGroup(group.value.numer, options.withStyle(nstyle));
- var numerreset = makeSpan([fstyle.reset(), nstyle.cls()], [numer]);
-
- var denom = buildGroup(group.value.denom, options.withStyle(dstyle));
- var denomreset = makeSpan([fstyle.reset(), dstyle.cls()], [denom]);
-
- var ruleWidth;
- if (group.value.hasBarLine) {
- ruleWidth = fontMetrics.metrics.defaultRuleThickness /
- options.style.sizeMultiplier;
- } else {
- ruleWidth = 0;
- }
-
- // Rule 15b
- var numShift;
- var clearance;
- var denomShift;
- if (fstyle.size === Style.DISPLAY.size) {
- numShift = fontMetrics.metrics.num1;
- if (ruleWidth > 0) {
- clearance = 3 * ruleWidth;
- } else {
- clearance = 7 * fontMetrics.metrics.defaultRuleThickness;
- }
- denomShift = fontMetrics.metrics.denom1;
- } else {
- if (ruleWidth > 0) {
- numShift = fontMetrics.metrics.num2;
- clearance = ruleWidth;
- } else {
- numShift = fontMetrics.metrics.num3;
- clearance = 3 * fontMetrics.metrics.defaultRuleThickness;
- }
- denomShift = fontMetrics.metrics.denom2;
- }
-
- var frac;
- if (ruleWidth === 0) {
- // Rule 15c
- var candiateClearance =
- (numShift - numer.depth) - (denom.height - denomShift);
- if (candiateClearance < clearance) {
- numShift += 0.5 * (clearance - candiateClearance);
- denomShift += 0.5 * (clearance - candiateClearance);
- }
-
- frac = buildCommon.makeVList([
- {type: "elem", elem: denomreset, shift: denomShift},
- {type: "elem", elem: numerreset, shift: -numShift},
- ], "individualShift", null, options);
- } else {
- // Rule 15d
- var axisHeight = fontMetrics.metrics.axisHeight;
-
- if ((numShift - numer.depth) - (axisHeight + 0.5 * ruleWidth) <
- clearance) {
- numShift +=
- clearance - ((numShift - numer.depth) -
- (axisHeight + 0.5 * ruleWidth));
- }
-
- if ((axisHeight - 0.5 * ruleWidth) - (denom.height - denomShift) <
- clearance) {
- denomShift +=
- clearance - ((axisHeight - 0.5 * ruleWidth) -
- (denom.height - denomShift));
- }
-
- var mid = makeSpan(
- [options.style.reset(), Style.TEXT.cls(), "frac-line"]);
- // Manually set the height of the line because its height is
- // created in CSS
- mid.height = ruleWidth;
-
- var midShift = -(axisHeight - 0.5 * ruleWidth);
-
- frac = buildCommon.makeVList([
- {type: "elem", elem: denomreset, shift: denomShift},
- {type: "elem", elem: mid, shift: midShift},
- {type: "elem", elem: numerreset, shift: -numShift},
- ], "individualShift", null, options);
- }
-
- // Since we manually change the style sometimes (with \dfrac or \tfrac),
- // account for the possible size change here.
- frac.height *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
- frac.depth *= fstyle.sizeMultiplier / options.style.sizeMultiplier;
-
- // Rule 15e
- var delimSize;
- if (fstyle.size === Style.DISPLAY.size) {
- delimSize = fontMetrics.metrics.delim1;
- } else {
- delimSize = fontMetrics.metrics.getDelim2(fstyle);
- }
-
- var leftDelim;
- var rightDelim;
- if (group.value.leftDelim == null) {
- leftDelim = makeNullDelimiter(options);
- } else {
- leftDelim = delimiter.customSizedDelim(
- group.value.leftDelim, delimSize, true,
- options.withStyle(fstyle), group.mode);
- }
- if (group.value.rightDelim == null) {
- rightDelim = makeNullDelimiter(options);
- } else {
- rightDelim = delimiter.customSizedDelim(
- group.value.rightDelim, delimSize, true,
- options.withStyle(fstyle), group.mode);
- }
-
- return makeSpan(
- ["mord", options.style.reset(), fstyle.cls()],
- [leftDelim, makeSpan(["mfrac"], [frac]), rightDelim],
- options.getColor());
-};
-
-groupTypes.array = function(group, options, prev) {
- var r;
- var c;
- var nr = group.value.body.length;
- var nc = 0;
- var body = new Array(nr);
-
- // Horizontal spacing
- var pt = 1 / fontMetrics.metrics.ptPerEm;
- var arraycolsep = 5 * pt; // \arraycolsep in article.cls
-
- // Vertical spacing
- var baselineskip = 12 * pt; // see size10.clo
- // Default \arraystretch from lttab.dtx
- // TODO(gagern): may get redefined once we have user-defined macros
- var arraystretch = utils.deflt(group.value.arraystretch, 1);
- var arrayskip = arraystretch * baselineskip;
- var arstrutHeight = 0.7 * arrayskip; // \strutbox in ltfsstrc.dtx and
- var arstrutDepth = 0.3 * arrayskip; // \@arstrutbox in lttab.dtx
-
- var totalHeight = 0;
- for (r = 0; r < group.value.body.length; ++r) {
- var inrow = group.value.body[r];
- var height = arstrutHeight; // \@array adds an \@arstrut
- var depth = arstrutDepth; // to each tow (via the template)
-
- if (nc < inrow.length) {
- nc = inrow.length;
- }
-
- var outrow = new Array(inrow.length);
- for (c = 0; c < inrow.length; ++c) {
- var elt = buildGroup(inrow[c], options);
- if (depth < elt.depth) {
- depth = elt.depth;
- }
- if (height < elt.height) {
- height = elt.height;
- }
- outrow[c] = elt;
- }
-
- var gap = 0;
- if (group.value.rowGaps[r]) {
- gap = group.value.rowGaps[r].value;
- switch (gap.unit) {
- case "em":
- gap = gap.number;
- break;
- case "ex":
- gap = gap.number * fontMetrics.metrics.emPerEx;
- break;
- default:
- console.error("Can't handle unit " + gap.unit);
- gap = 0;
- }
- if (gap > 0) { // \@argarraycr
- gap += arstrutDepth;
- if (depth < gap) {
- depth = gap; // \@xargarraycr
- }
- gap = 0;
- }
- }
-
- outrow.height = height;
- outrow.depth = depth;
- totalHeight += height;
- outrow.pos = totalHeight;
- totalHeight += depth + gap; // \@yargarraycr
- body[r] = outrow;
- }
-
- var offset = totalHeight / 2 + fontMetrics.metrics.axisHeight;
- var colDescriptions = group.value.cols || [];
- var cols = [];
- var colSep;
- var colDescrNum;
- for (c = 0, colDescrNum = 0;
- // Continue while either there are more columns or more column
- // descriptions, so trailing separators don't get lost.
- c < nc || colDescrNum < colDescriptions.length;
- ++c, ++colDescrNum) {
-
- var colDescr = colDescriptions[colDescrNum] || {};
-
- var firstSeparator = true;
- while (colDescr.type === "separator") {
- // If there is more than one separator in a row, add a space
- // between them.
- if (!firstSeparator) {
- colSep = makeSpan(["arraycolsep"], []);
- colSep.style.width =
- fontMetrics.metrics.doubleRuleSep + "em";
- cols.push(colSep);
- }
-
- if (colDescr.separator === "|") {
- var separator = makeSpan(
- ["vertical-separator"],
- []);
- separator.style.height = totalHeight + "em";
- separator.style.verticalAlign =
- -(totalHeight - offset) + "em";
-
- cols.push(separator);
- } else {
- throw new ParseError(
- "Invalid separator type: " + colDescr.separator);
- }
-
- colDescrNum++;
- colDescr = colDescriptions[colDescrNum] || {};
- firstSeparator = false;
- }
-
- if (c >= nc) {
- continue;
- }
-
- var sepwidth;
- if (c > 0 || group.value.hskipBeforeAndAfter) {
- sepwidth = utils.deflt(colDescr.pregap, arraycolsep);
- if (sepwidth !== 0) {
- colSep = makeSpan(["arraycolsep"], []);
- colSep.style.width = sepwidth + "em";
- cols.push(colSep);
- }
- }
-
- var col = [];
- for (r = 0; r < nr; ++r) {
- var row = body[r];
- var elem = row[c];
- if (!elem) {
- continue;
- }
- var shift = row.pos - offset;
- elem.depth = row.depth;
- elem.height = row.height;
- col.push({type: "elem", elem: elem, shift: shift});
- }
-
- col = buildCommon.makeVList(col, "individualShift", null, options);
- col = makeSpan(
- ["col-align-" + (colDescr.align || "c")],
- [col]);
- cols.push(col);
-
- if (c < nc - 1 || group.value.hskipBeforeAndAfter) {
- sepwidth = utils.deflt(colDescr.postgap, arraycolsep);
- if (sepwidth !== 0) {
- colSep = makeSpan(["arraycolsep"], []);
- colSep.style.width = sepwidth + "em";
- cols.push(colSep);
- }
- }
- }
- body = makeSpan(["mtable"], cols);
- return makeSpan(["mord"], [body], options.getColor());
-};
-
-groupTypes.spacing = function(group, options, prev) {
- if (group.value === "\\ " || group.value === "\\space" ||
- group.value === " " || group.value === "~") {
- // Spaces are generated by adding an actual space. Each of these
- // things has an entry in the symbols table, so these will be turned
- // into appropriate outputs.
- return makeSpan(
- ["mord", "mspace"],
- [buildCommon.mathsym(group.value, group.mode)]
- );
- } else {
- // Other kinds of spaces are of arbitrary width. We use CSS to
- // generate these.
- return makeSpan(
- ["mord", "mspace",
- buildCommon.spacingFunctions[group.value].className]);
- }
-};
-
-groupTypes.llap = function(group, options, prev) {
- var inner = makeSpan(
- ["inner"], [buildGroup(group.value.body, options.reset())]);
- var fix = makeSpan(["fix"], []);
- return makeSpan(
- ["llap", options.style.cls()], [inner, fix]);
-};
-
-groupTypes.rlap = function(group, options, prev) {
- var inner = makeSpan(
- ["inner"], [buildGroup(group.value.body, options.reset())]);
- var fix = makeSpan(["fix"], []);
- return makeSpan(
- ["rlap", options.style.cls()], [inner, fix]);
-};
-
-groupTypes.op = function(group, options, prev) {
- // Operators are handled in the TeXbook pg. 443-444, rule 13(a).
- var supGroup;
- var subGroup;
- var hasLimits = false;
- if (group.type === "supsub" ) {
- // If we have limits, supsub will pass us its group to handle. Pull
- // out the superscript and subscript and set the group to the op in
- // its base.
- supGroup = group.value.sup;
- subGroup = group.value.sub;
- group = group.value.base;
- hasLimits = true;
- }
-
- // Most operators have a large successor symbol, but these don't.
- var noSuccessor = [
- "\\smallint",
- ];
-
- var large = false;
- if (options.style.size === Style.DISPLAY.size &&
- group.value.symbol &&
- !utils.contains(noSuccessor, group.value.body)) {
-
- // Most symbol operators get larger in displaystyle (rule 13)
- large = true;
- }
-
- var base;
- var baseShift = 0;
- var slant = 0;
- if (group.value.symbol) {
- // If this is a symbol, create the symbol.
- var style = large ? "Size2-Regular" : "Size1-Regular";
- base = buildCommon.makeSymbol(
- group.value.body, style, "math", options.getColor(),
- ["op-symbol", large ? "large-op" : "small-op", "mop"]);
-
- // Shift the symbol so its center lies on the axis (rule 13). It
- // appears that our fonts have the centers of the symbols already
- // almost on the axis, so these numbers are very small. Note we
- // don't actually apply this here, but instead it is used either in
- // the vlist creation or separately when there are no limits.
- baseShift = (base.height - base.depth) / 2 -
- fontMetrics.metrics.axisHeight *
- options.style.sizeMultiplier;
-
- // The slant of the symbol is just its italic correction.
- slant = base.italic;
- } else {
- // Otherwise, this is a text operator. Build the text from the
- // operator's name.
- // TODO(emily): Add a space in the middle of some of these
- // operators, like \limsup
- var output = [];
- for (var i = 1; i < group.value.body.length; i++) {
- output.push(buildCommon.mathsym(group.value.body[i], group.mode));
- }
- base = makeSpan(["mop"], output, options.getColor());
- }
-
- if (hasLimits) {
- // IE 8 clips \int if it is in a display: inline-block. We wrap it
- // in a new span so it is an inline, and works.
- base = makeSpan([], [base]);
-
- var supmid;
- var supKern;
- var submid;
- var subKern;
- // We manually have to handle the superscripts and subscripts. This,
- // aside from the kern calculations, is copied from supsub.
- if (supGroup) {
- var sup = buildGroup(
- supGroup, options.withStyle(options.style.sup()));
- supmid = makeSpan(
- [options.style.reset(), options.style.sup().cls()], [sup]);
-
- supKern = Math.max(
- fontMetrics.metrics.bigOpSpacing1,
- fontMetrics.metrics.bigOpSpacing3 - sup.depth);
- }
-
- if (subGroup) {
- var sub = buildGroup(
- subGroup, options.withStyle(options.style.sub()));
- submid = makeSpan(
- [options.style.reset(), options.style.sub().cls()],
- [sub]);
-
- subKern = Math.max(
- fontMetrics.metrics.bigOpSpacing2,
- fontMetrics.metrics.bigOpSpacing4 - sub.height);
- }
-
- // Build the final group as a vlist of the possible subscript, base,
- // and possible superscript.
- var finalGroup;
- var top;
- var bottom;
- if (!supGroup) {
- top = base.height - baseShift;
-
- finalGroup = buildCommon.makeVList([
- {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
- {type: "elem", elem: submid},
- {type: "kern", size: subKern},
- {type: "elem", elem: base},
- ], "top", top, options);
-
- // Here, we shift the limits by the slant of the symbol. Note
- // that we are supposed to shift the limits by 1/2 of the slant,
- // but since we are centering the limits adding a full slant of
- // margin will shift by 1/2 that.
- finalGroup.children[0].style.marginLeft = -slant + "em";
- } else if (!subGroup) {
- bottom = base.depth + baseShift;
-
- finalGroup = buildCommon.makeVList([
- {type: "elem", elem: base},
- {type: "kern", size: supKern},
- {type: "elem", elem: supmid},
- {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
- ], "bottom", bottom, options);
-
- // See comment above about slants
- finalGroup.children[1].style.marginLeft = slant + "em";
- } else if (!supGroup && !subGroup) {
- // This case probably shouldn't occur (this would mean the
- // supsub was sending us a group with no superscript or
- // subscript) but be safe.
- return base;
- } else {
- bottom = fontMetrics.metrics.bigOpSpacing5 +
- submid.height + submid.depth +
- subKern +
- base.depth + baseShift;
-
- finalGroup = buildCommon.makeVList([
- {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
- {type: "elem", elem: submid},
- {type: "kern", size: subKern},
- {type: "elem", elem: base},
- {type: "kern", size: supKern},
- {type: "elem", elem: supmid},
- {type: "kern", size: fontMetrics.metrics.bigOpSpacing5},
- ], "bottom", bottom, options);
-
- // See comment above about slants
- finalGroup.children[0].style.marginLeft = -slant + "em";
- finalGroup.children[2].style.marginLeft = slant + "em";
- }
-
- return makeSpan(["mop", "op-limits"], [finalGroup]);
- } else {
- if (group.value.symbol) {
- base.style.top = baseShift + "em";
- }
-
- return base;
- }
-};
-
-groupTypes.katex = function(group, options, prev) {
- // The KaTeX logo. The offsets for the K and a were chosen to look
- // good, but the offsets for the T, E, and X were taken from the
- // definition of \TeX in TeX (see TeXbook pg. 356)
- var k = makeSpan(
- ["k"], [buildCommon.mathsym("K", group.mode)]);
- var a = makeSpan(
- ["a"], [buildCommon.mathsym("A", group.mode)]);
-
- a.height = (a.height + 0.2) * 0.75;
- a.depth = (a.height - 0.2) * 0.75;
-
- var t = makeSpan(
- ["t"], [buildCommon.mathsym("T", group.mode)]);
- var e = makeSpan(
- ["e"], [buildCommon.mathsym("E", group.mode)]);
-
- e.height = (e.height - 0.2155);
- e.depth = (e.depth + 0.2155);
-
- var x = makeSpan(
- ["x"], [buildCommon.mathsym("X", group.mode)]);
-
- return makeSpan(
- ["katex-logo", "mord"], [k, a, t, e, x], options.getColor());
-};
-
-groupTypes.overline = function(group, options, prev) {
- // Overlines are handled in the TeXbook pg 443, Rule 9.
-
- // Build the inner group in the cramped style.
- var innerGroup = buildGroup(group.value.body,
- options.withStyle(options.style.cramp()));
-
- var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
- options.style.sizeMultiplier;
-
- // Create the line above the body
- var line = makeSpan(
- [options.style.reset(), Style.TEXT.cls(), "overline-line"]);
- line.height = ruleWidth;
- line.maxFontSize = 1.0;
-
- // Generate the vlist, with the appropriate kerns
- var vlist = buildCommon.makeVList([
- {type: "elem", elem: innerGroup},
- {type: "kern", size: 3 * ruleWidth},
- {type: "elem", elem: line},
- {type: "kern", size: ruleWidth},
- ], "firstBaseline", null, options);
-
- return makeSpan(["overline", "mord"], [vlist], options.getColor());
-};
-
-groupTypes.underline = function(group, options, prev) {
- // Underlines are handled in the TeXbook pg 443, Rule 10.
-
- // Build the inner group.
- var innerGroup = buildGroup(group.value.body, options);
-
- var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
- options.style.sizeMultiplier;
-
- // Create the line above the body
- var line = makeSpan(
- [options.style.reset(), Style.TEXT.cls(), "underline-line"]);
- line.height = ruleWidth;
- line.maxFontSize = 1.0;
-
- // Generate the vlist, with the appropriate kerns
- var vlist = buildCommon.makeVList([
- {type: "kern", size: ruleWidth},
- {type: "elem", elem: line},
- {type: "kern", size: 3 * ruleWidth},
- {type: "elem", elem: innerGroup},
- ], "top", innerGroup.height, options);
-
- return makeSpan(["underline", "mord"], [vlist], options.getColor());
-};
-
-groupTypes.sqrt = function(group, options, prev) {
- // Square roots are handled in the TeXbook pg. 443, Rule 11.
-
- // First, we do the same steps as in overline to build the inner group
- // and line
- var inner = buildGroup(group.value.body,
- options.withStyle(options.style.cramp()));
-
- var ruleWidth = fontMetrics.metrics.defaultRuleThickness /
- options.style.sizeMultiplier;
-
- var line = makeSpan(
- [options.style.reset(), Style.TEXT.cls(), "sqrt-line"], [],
- options.getColor());
- line.height = ruleWidth;
- line.maxFontSize = 1.0;
-
- var phi = ruleWidth;
- if (options.style.id < Style.TEXT.id) {
- phi = fontMetrics.metrics.xHeight;
- }
-
- // Calculate the clearance between the body and line
- var lineClearance = ruleWidth + phi / 4;
-
- var innerHeight =
- (inner.height + inner.depth) * options.style.sizeMultiplier;
- var minDelimiterHeight = innerHeight + lineClearance + ruleWidth;
-
- // Create a \surd delimiter of the required minimum size
- var delim = makeSpan(["sqrt-sign"], [
- delimiter.customSizedDelim("\\surd", minDelimiterHeight,
- false, options, group.mode)],
- options.getColor());
-
- var delimDepth = (delim.height + delim.depth) - ruleWidth;
-
- // Adjust the clearance based on the delimiter size
- if (delimDepth > inner.height + inner.depth + lineClearance) {
- lineClearance =
- (lineClearance + delimDepth - inner.height - inner.depth) / 2;
- }
-
- // Shift the delimiter so that its top lines up with the top of the line
- var delimShift = -(inner.height + lineClearance + ruleWidth) + delim.height;
- delim.style.top = delimShift + "em";
- delim.height -= delimShift;
- delim.depth += delimShift;
-
- // We add a special case here, because even when `inner` is empty, we
- // still get a line. So, we use a simple heuristic to decide if we
- // should omit the body entirely. (note this doesn't work for something
- // like `\sqrt{\rlap{x}}`, but if someone is doing that they deserve for
- // it not to work.
- var body;
- if (inner.height === 0 && inner.depth === 0) {
- body = makeSpan();
- } else {
- body = buildCommon.makeVList([
- {type: "elem", elem: inner},
- {type: "kern", size: lineClearance},
- {type: "elem", elem: line},
- {type: "kern", size: ruleWidth},
- ], "firstBaseline", null, options);
- }
-
- if (!group.value.index) {
- return makeSpan(["sqrt", "mord"], [delim, body]);
- } else {
- // Handle the optional root index
-
- // The index is always in scriptscript style
- var root = buildGroup(
- group.value.index,
- options.withStyle(Style.SCRIPTSCRIPT));
- var rootWrap = makeSpan(
- [options.style.reset(), Style.SCRIPTSCRIPT.cls()],
- [root]);
-
- // Figure out the height and depth of the inner part
- var innerRootHeight = Math.max(delim.height, body.height);
- var innerRootDepth = Math.max(delim.depth, body.depth);
-
- // The amount the index is shifted by. This is taken from the TeX
- // source, in the definition of `\r@@t`.
- var toShift = 0.6 * (innerRootHeight - innerRootDepth);
-
- // Build a VList with the superscript shifted up correctly
- var rootVList = buildCommon.makeVList(
- [{type: "elem", elem: rootWrap}],
- "shift", -toShift, options);
- // Add a class surrounding it so we can add on the appropriate
- // kerning
- var rootVListWrap = makeSpan(["root"], [rootVList]);
-
- return makeSpan(["sqrt", "mord"], [rootVListWrap, delim, body]);
- }
-};
-
-groupTypes.sizing = function(group, options, prev) {
- // Handle sizing operators like \Huge. Real TeX doesn't actually allow
- // these functions inside of math expressions, so we do some special
- // handling.
- var inner = buildExpression(group.value.value,
- options.withSize(group.value.size), prev);
-
- var span = makeSpan(["mord"],
- [makeSpan(["sizing", "reset-" + options.size, group.value.size,
- options.style.cls()],
- inner)]);
-
- // Calculate the correct maxFontSize manually
- var fontSize = buildCommon.sizingMultiplier[group.value.size];
- span.maxFontSize = fontSize * options.style.sizeMultiplier;
-
- return span;
-};
-
-groupTypes.styling = function(group, options, prev) {
- // Style changes are handled in the TeXbook on pg. 442, Rule 3.
-
- // Figure out what style we're changing to.
- var style = {
- "display": Style.DISPLAY,
- "text": Style.TEXT,
- "script": Style.SCRIPT,
- "scriptscript": Style.SCRIPTSCRIPT,
- };
-
- var newStyle = style[group.value.style];
-
- // Build the inner expression in the new style.
- var inner = buildExpression(
- group.value.value, options.withStyle(newStyle), prev);
-
- return makeSpan([options.style.reset(), newStyle.cls()], inner);
-};
-
-groupTypes.font = function(group, options, prev) {
- var font = group.value.font;
- return buildGroup(group.value.body, options.withFont(font), prev);
-};
-
-groupTypes.delimsizing = function(group, options, prev) {
- var delim = group.value.value;
-
- if (delim === ".") {
- // Empty delimiters still count as elements, even though they don't
- // show anything.
- return makeSpan([groupToType[group.value.delimType]]);
- }
-
- // Use delimiter.sizedDelim to generate the delimiter.
- return makeSpan(
- [groupToType[group.value.delimType]],
- [delimiter.sizedDelim(
- delim, group.value.size, options, group.mode)]);
-};
-
-groupTypes.leftright = function(group, options, prev) {
- // Build the inner expression
- var inner = buildExpression(group.value.body, options.reset());
-
- var innerHeight = 0;
- var innerDepth = 0;
-
- // Calculate its height and depth
- for (var i = 0; i < inner.length; i++) {
- innerHeight = Math.max(inner[i].height, innerHeight);
- innerDepth = Math.max(inner[i].depth, innerDepth);
- }
-
- // The size of delimiters is the same, regardless of what style we are
- // in. Thus, to correctly calculate the size of delimiter we need around
- // a group, we scale down the inner size based on the size.
- innerHeight *= options.style.sizeMultiplier;
- innerDepth *= options.style.sizeMultiplier;
-
- var leftDelim;
- if (group.value.left === ".") {
- // Empty delimiters in \left and \right make null delimiter spaces.
- leftDelim = makeNullDelimiter(options);
- } else {
- // Otherwise, use leftRightDelim to generate the correct sized
- // delimiter.
- leftDelim = delimiter.leftRightDelim(
- group.value.left, innerHeight, innerDepth, options,
- group.mode);
- }
- // Add it to the beginning of the expression
- inner.unshift(leftDelim);
-
- var rightDelim;
- // Same for the right delimiter
- if (group.value.right === ".") {
- rightDelim = makeNullDelimiter(options);
- } else {
- rightDelim = delimiter.leftRightDelim(
- group.value.right, innerHeight, innerDepth, options,
- group.mode);
- }
- // Add it to the end of the expression.
- inner.push(rightDelim);
-
- return makeSpan(
- ["minner", options.style.cls()], inner, options.getColor());
-};
-
-groupTypes.rule = function(group, options, prev) {
- // Make an empty span for the rule
- var rule = makeSpan(["mord", "rule"], [], options.getColor());
-
- // Calculate the shift, width, and height of the rule, and account for units
- var shift = 0;
- if (group.value.shift) {
- shift = group.value.shift.number;
- if (group.value.shift.unit === "ex") {
- shift *= fontMetrics.metrics.xHeight;
- }
- }
-
- var width = group.value.width.number;
- if (group.value.width.unit === "ex") {
- width *= fontMetrics.metrics.xHeight;
- }
-
- var height = group.value.height.number;
- if (group.value.height.unit === "ex") {
- height *= fontMetrics.metrics.xHeight;
- }
-
- // The sizes of rules are absolute, so make it larger if we are in a
- // smaller style.
- shift /= options.style.sizeMultiplier;
- width /= options.style.sizeMultiplier;
- height /= options.style.sizeMultiplier;
-
- // Style the rule to the right size
- rule.style.borderRightWidth = width + "em";
- rule.style.borderTopWidth = height + "em";
- rule.style.bottom = shift + "em";
-
- // Record the height and width
- rule.width = width;
- rule.height = height + shift;
- rule.depth = -shift;
-
- return rule;
-};
-
-groupTypes.kern = function(group, options, prev) {
- // Make an empty span for the rule
- var rule = makeSpan(["mord", "rule"], [], options.getColor());
-
- var dimension = 0;
- if (group.value.dimension) {
- dimension = group.value.dimension.number;
- if (group.value.dimension.unit === "ex") {
- dimension *= fontMetrics.metrics.xHeight;
- }
- }
-
- dimension /= options.style.sizeMultiplier;
-
- rule.style.marginLeft = dimension + "em";
-
- return rule;
-};
-
-groupTypes.accent = function(group, options, prev) {
- // Accents are handled in the TeXbook pg. 443, rule 12.
- var base = group.value.base;
-
- var supsubGroup;
- if (group.type === "supsub") {
- // If our base is a character box, and we have superscripts and
- // subscripts, the supsub will defer to us. In particular, we want
- // to attach the superscripts and subscripts to the inner body (so
- // that the position of the superscripts and subscripts won't be
- // affected by the height of the accent). We accomplish this by
- // sticking the base of the accent into the base of the supsub, and
- // rendering that, while keeping track of where the accent is.
-
- // The supsub group is the group that was passed in
- var supsub = group;
- // The real accent group is the base of the supsub group
- group = supsub.value.base;
- // The character box is the base of the accent group
- base = group.value.base;
- // Stick the character box into the base of the supsub group
- supsub.value.base = base;
-
- // Rerender the supsub group with its new base, and store that
- // result.
- supsubGroup = buildGroup(
- supsub, options.reset(), prev);
- }
-
- // Build the base group
- var body = buildGroup(
- base, options.withStyle(options.style.cramp()));
-
- // Calculate the skew of the accent. This is based on the line "If the
- // nucleus is not a single character, let s = 0; otherwise set s to the
- // kern amount for the nucleus followed by the \skewchar of its font."
- // Note that our skew metrics are just the kern between each character
- // and the skewchar.
- var skew;
- if (isCharacterBox(base)) {
- // If the base is a character box, then we want the skew of the
- // innermost character. To do that, we find the innermost character:
- var baseChar = getBaseElem(base);
- // Then, we render its group to get the symbol inside it
- var baseGroup = buildGroup(
- baseChar, options.withStyle(options.style.cramp()));
- // Finally, we pull the skew off of the symbol.
- skew = baseGroup.skew;
- // Note that we now throw away baseGroup, because the layers we
- // removed with getBaseElem might contain things like \color which
- // we can't get rid of.
- // TODO(emily): Find a better way to get the skew
- } else {
- skew = 0;
- }
-
- // calculate the amount of space between the body and the accent
- var clearance = Math.min(body.height, fontMetrics.metrics.xHeight);
-
- // Build the accent
- var accent = buildCommon.makeSymbol(
- group.value.accent, "Main-Regular", "math", options.getColor());
- // Remove the italic correction of the accent, because it only serves to
- // shift the accent over to a place we don't want.
- accent.italic = 0;
-
- // The \vec character that the fonts use is a combining character, and
- // thus shows up much too far to the left. To account for this, we add a
- // specific class which shifts the accent over to where we want it.
- // TODO(emily): Fix this in a better way, like by changing the font
- var vecClass = group.value.accent === "\\vec" ? "accent-vec" : null;
-
- var accentBody = makeSpan(["accent-body", vecClass], [
- makeSpan([], [accent])]);
-
- accentBody = buildCommon.makeVList([
- {type: "elem", elem: body},
- {type: "kern", size: -clearance},
- {type: "elem", elem: accentBody},
- ], "firstBaseline", null, options);
-
- // Shift the accent over by the skew. Note we shift by twice the skew
- // because we are centering the accent, so by adding 2*skew to the left,
- // we shift it to the right by 1*skew.
- accentBody.children[1].style.marginLeft = 2 * skew + "em";
-
- var accentWrap = makeSpan(["mord", "accent"], [accentBody]);
-
- if (supsubGroup) {
- // Here, we replace the "base" child of the supsub with our newly
- // generated accent.
- supsubGroup.children[0] = accentWrap;
-
- // Since we don't rerun the height calculation after replacing the
- // accent, we manually recalculate height.
- supsubGroup.height = Math.max(accentWrap.height, supsubGroup.height);
-
- // Accents should always be ords, even when their innards are not.
- supsubGroup.classes[0] = "mord";
-
- return supsubGroup;
- } else {
- return accentWrap;
- }
-};
-
-groupTypes.phantom = function(group, options, prev) {
- var elements = buildExpression(
- group.value.value,
- options.withPhantom(),
- prev
- );
-
- // \phantom isn't supposed to affect the elements it contains.
- // See "color" for more details.
- return new buildCommon.makeFragment(elements);
-};
-
-/**
- * buildGroup is the function that takes a group and calls the correct groupType
- * function for it. It also handles the interaction of size and style changes
- * between parents and children.
- */
-var buildGroup = function(group, options, prev) {
- if (!group) {
- return makeSpan();
- }
-
- if (groupTypes[group.type]) {
- // Call the groupTypes function
- var groupNode = groupTypes[group.type](group, options, prev);
- var multiplier;
-
- // If the style changed between the parent and the current group,
- // account for the size difference
- if (options.style !== options.parentStyle) {
- multiplier = options.style.sizeMultiplier /
- options.parentStyle.sizeMultiplier;
-
- groupNode.height *= multiplier;
- groupNode.depth *= multiplier;
- }
-
- // If the size changed between the parent and the current group, account
- // for that size difference.
- if (options.size !== options.parentSize) {
- multiplier = buildCommon.sizingMultiplier[options.size] /
- buildCommon.sizingMultiplier[options.parentSize];
-
- groupNode.height *= multiplier;
- groupNode.depth *= multiplier;
- }
-
- return groupNode;
- } else {
- throw new ParseError(
- "Got group of unknown type: '" + group.type + "'");
- }
-};
-
-/**
- * Take an entire parse tree, and build it into an appropriate set of HTML
- * nodes.
- */
-var buildHTML = function(tree, options) {
- // buildExpression is destructive, so we need to make a clone
- // of the incoming tree so that it isn't accidentally changed
- tree = JSON.parse(JSON.stringify(tree));
-
- // Build the expression contained in the tree
- var expression = buildExpression(tree, options);
- var body = makeSpan(["base", options.style.cls()], expression);
-
- // Add struts, which ensure that the top of the HTML element falls at the
- // height of the expression, and the bottom of the HTML element falls at the
- // depth of the expression.
- var topStrut = makeSpan(["strut"]);
- var bottomStrut = makeSpan(["strut", "bottom"]);
-
- topStrut.style.height = body.height + "em";
- bottomStrut.style.height = (body.height + body.depth) + "em";
- // We'd like to use `vertical-align: top` but in IE 9 this lowers the
- // baseline of the box to the bottom of this strut (instead staying in the
- // normal place) so we use an absolute value for vertical-align instead
- bottomStrut.style.verticalAlign = -body.depth + "em";
-
- // Wrap the struts and body together
- var htmlNode = makeSpan(["katex-html"], [topStrut, bottomStrut, body]);
-
- htmlNode.setAttribute("aria-hidden", "true");
-
- return htmlNode;
-};
-
-module.exports = buildHTML;
-
-},{"./ParseError":6,"./Style":9,"./buildCommon":10,"./delimiter":14,"./domTree":15,"./fontMetrics":17,"./utils":25}],12:[function(require,module,exports){
-/**
- * This file converts a parse tree into a cooresponding MathML tree. The main
- * entry point is the `buildMathML` function, which takes a parse tree from the
- * parser.
- */
-
-var buildCommon = require("./buildCommon");
-var fontMetrics = require("./fontMetrics");
-var mathMLTree = require("./mathMLTree");
-var ParseError = require("./ParseError");
-var symbols = require("./symbols");
-var utils = require("./utils");
-
-var makeSpan = buildCommon.makeSpan;
-var fontMap = buildCommon.fontMap;
-
-/**
- * Takes a symbol and converts it into a MathML text node after performing
- * optional replacement from symbols.js.
- */
-var makeText = function(text, mode) {
- if (symbols[mode][text] && symbols[mode][text].replace) {
- text = symbols[mode][text].replace;
- }
-
- return new mathMLTree.TextNode(text);
-};
-
-/**
- * Returns the math variant as a string or null if none is required.
- */
-var getVariant = function(group, options) {
- var font = options.font;
- if (!font) {
- return null;
- }
-
- var mode = group.mode;
- if (font === "mathit") {
- return "italic";
- }
-
- var value = group.value;
- if (utils.contains(["\\imath", "\\jmath"], value)) {
- return null;
- }
-
- if (symbols[mode][value] && symbols[mode][value].replace) {
- value = symbols[mode][value].replace;
- }
-
- var fontName = fontMap[font].fontName;
- if (fontMetrics.getCharacterMetrics(value, fontName)) {
- return fontMap[options.font].variant;
- }
-
- return null;
-};
-
-/**
- * Functions for handling the different types of groups found in the parse
- * tree. Each function should take a parse group and return a MathML node.
- */
-var groupTypes = {};
-
-groupTypes.mathord = function(group, options) {
- var node = new mathMLTree.MathNode(
- "mi",
- [makeText(group.value, group.mode)]);
-
- var variant = getVariant(group, options);
- if (variant) {
- node.setAttribute("mathvariant", variant);
- }
- return node;
-};
-
-groupTypes.textord = function(group, options) {
- var text = makeText(group.value, group.mode);
-
- var variant = getVariant(group, options) || "normal";
-
- var node;
- if (/[0-9]/.test(group.value)) {
- // TODO(kevinb) merge adjacent <mn> nodes
- // do it as a post processing step
- node = new mathMLTree.MathNode("mn", [text]);
- if (options.font) {
- node.setAttribute("mathvariant", variant);
- }
- } else {
- node = new mathMLTree.MathNode("mi", [text]);
- node.setAttribute("mathvariant", variant);
- }
-
- return node;
-};
-
-groupTypes.bin = function(group) {
- var node = new mathMLTree.MathNode(
- "mo", [makeText(group.value, group.mode)]);
-
- return node;
-};
-
-groupTypes.rel = function(group) {
- var node = new mathMLTree.MathNode(
- "mo", [makeText(group.value, group.mode)]);
-
- return node;
-};
-
-groupTypes.open = function(group) {
- var node = new mathMLTree.MathNode(
- "mo", [makeText(group.value, group.mode)]);
-
- return node;
-};
-
-groupTypes.close = function(group) {
- var node = new mathMLTree.MathNode(
- "mo", [makeText(group.value, group.mode)]);
-
- return node;
-};
-
-groupTypes.inner = function(group) {
- var node = new mathMLTree.MathNode(
- "mo", [makeText(group.value, group.mode)]);
-
- return node;
-};
-
-groupTypes.punct = function(group) {
- var node = new mathMLTree.MathNode(
- "mo", [makeText(group.value, group.mode)]);
-
- node.setAttribute("separator", "true");
-
- return node;
-};
-
-groupTypes.ordgroup = function(group, options) {
- var inner = buildExpression(group.value, options);
-
- var node = new mathMLTree.MathNode("mrow", inner);
-
- return node;
-};
-
-groupTypes.text = function(group, options) {
- var inner = buildExpression(group.value.body, options);
-
- var node = new mathMLTree.MathNode("mtext", inner);
-
- return node;
-};
-
-groupTypes.color = function(group, options) {
- var inner = buildExpression(group.value.value, options);
-
- var node = new mathMLTree.MathNode("mstyle", inner);
-
- node.setAttribute("mathcolor", group.value.color);
-
- return node;
-};
-
-groupTypes.supsub = function(group, options) {
- var children = [buildGroup(group.value.base, options)];
-
- if (group.value.sub) {
- children.push(buildGroup(group.value.sub, options));
- }
-
- if (group.value.sup) {
- children.push(buildGroup(group.value.sup, options));
- }
-
- var nodeType;
- if (!group.value.sub) {
- nodeType = "msup";
- } else if (!group.value.sup) {
- nodeType = "msub";
- } else {
- nodeType = "msubsup";
- }
-
- var node = new mathMLTree.MathNode(nodeType, children);
-
- return node;
-};
-
-groupTypes.genfrac = function(group, options) {
- var node = new mathMLTree.MathNode(
- "mfrac",
- [buildGroup(group.value.numer, options),
- buildGroup(group.value.denom, options)]);
-
- if (!group.value.hasBarLine) {
- node.setAttribute("linethickness", "0px");
- }
-
- if (group.value.leftDelim != null || group.value.rightDelim != null) {
- var withDelims = [];
-
- if (group.value.leftDelim != null) {
- var leftOp = new mathMLTree.MathNode(
- "mo", [new mathMLTree.TextNode(group.value.leftDelim)]);
-
- leftOp.setAttribute("fence", "true");
-
- withDelims.push(leftOp);
- }
-
- withDelims.push(node);
-
- if (group.value.rightDelim != null) {
- var rightOp = new mathMLTree.MathNode(
- "mo", [new mathMLTree.TextNode(group.value.rightDelim)]);
-
- rightOp.setAttribute("fence", "true");
-
- withDelims.push(rightOp);
- }
-
- var outerNode = new mathMLTree.MathNode("mrow", withDelims);
-
- return outerNode;
- }
-
- return node;
-};
-
-groupTypes.array = function(group, options) {
- return new mathMLTree.MathNode(
- "mtable", group.value.body.map(function(row) {
- return new mathMLTree.MathNode(
- "mtr", row.map(function(cell) {
- return new mathMLTree.MathNode(
- "mtd", [buildGroup(cell, options)]);
- }));
- }));
-};
-
-groupTypes.sqrt = function(group, options) {
- var node;
- if (group.value.index) {
- node = new mathMLTree.MathNode(
- "mroot", [
- buildGroup(group.value.body, options),
- buildGroup(group.value.index, options),
- ]);
- } else {
- node = new mathMLTree.MathNode(
- "msqrt", [buildGroup(group.value.body, options)]);
- }
-
- return node;
-};
-
-groupTypes.leftright = function(group, options) {
- var inner = buildExpression(group.value.body, options);
-
- if (group.value.left !== ".") {
- var leftNode = new mathMLTree.MathNode(
- "mo", [makeText(group.value.left, group.mode)]);
-
- leftNode.setAttribute("fence", "true");
-
- inner.unshift(leftNode);
- }
-
- if (group.value.right !== ".") {
- var rightNode = new mathMLTree.MathNode(
- "mo", [makeText(group.value.right, group.mode)]);
-
- rightNode.setAttribute("fence", "true");
-
- inner.push(rightNode);
- }
-
- var outerNode = new mathMLTree.MathNode("mrow", inner);
-
- return outerNode;
-};
-
-groupTypes.accent = function(group, options) {
- var accentNode = new mathMLTree.MathNode(
- "mo", [makeText(group.value.accent, group.mode)]);
-
- var node = new mathMLTree.MathNode(
- "mover",
- [buildGroup(group.value.base, options),
- accentNode]);
-
- node.setAttribute("accent", "true");
-
- return node;
-};
-
-groupTypes.spacing = function(group) {
- var node;
-
- if (group.value === "\\ " || group.value === "\\space" ||
- group.value === " " || group.value === "~") {
- node = new mathMLTree.MathNode(
- "mtext", [new mathMLTree.TextNode("\u00a0")]);
- } else {
- node = new mathMLTree.MathNode("mspace");
-
- node.setAttribute(
- "width", buildCommon.spacingFunctions[group.value].size);
- }
-
- return node;
-};
-
-groupTypes.op = function(group) {
- var node;
-
- // TODO(emily): handle big operators using the `largeop` attribute
-
- if (group.value.symbol) {
- // This is a symbol. Just add the symbol.
- node = new mathMLTree.MathNode(
- "mo", [makeText(group.value.body, group.mode)]);
- } else {
- // This is a text operator. Add all of the characters from the
- // operator's name.
- // TODO(emily): Add a space in the middle of some of these
- // operators, like \limsup.
- node = new mathMLTree.MathNode(
- "mi", [new mathMLTree.TextNode(group.value.body.slice(1))]);
- }
-
- return node;
-};
-
-groupTypes.katex = function(group) {
- var node = new mathMLTree.MathNode(
- "mtext", [new mathMLTree.TextNode("KaTeX")]);
-
- return node;
-};
-
-groupTypes.font = function(group, options) {
- var font = group.value.font;
- return buildGroup(group.value.body, options.withFont(font));
-};
-
-groupTypes.delimsizing = function(group) {
- var children = [];
-
- if (group.value.value !== ".") {
- children.push(makeText(group.value.value, group.mode));
- }
-
- var node = new mathMLTree.MathNode("mo", children);
-
- if (group.value.delimType === "open" ||
- group.value.delimType === "close") {
- // Only some of the delimsizing functions act as fences, and they
- // return "open" or "close" delimTypes.
- node.setAttribute("fence", "true");
- } else {
- // Explicitly disable fencing if it's not a fence, to override the
- // defaults.
- node.setAttribute("fence", "false");
- }
-
- return node;
-};
-
-groupTypes.styling = function(group, options) {
- var inner = buildExpression(group.value.value, options);
-
- var node = new mathMLTree.MathNode("mstyle", inner);
-
- var styleAttributes = {
- "display": ["0", "true"],
- "text": ["0", "false"],
- "script": ["1", "false"],
- "scriptscript": ["2", "false"],
- };
-
- var attr = styleAttributes[group.value.style];
-
- node.setAttribute("scriptlevel", attr[0]);
- node.setAttribute("displaystyle", attr[1]);
-
- return node;
-};
-
-groupTypes.sizing = function(group, options) {
- var inner = buildExpression(group.value.value, options);
-
- var node = new mathMLTree.MathNode("mstyle", inner);
-
- // TODO(emily): This doesn't produce the correct size for nested size
- // changes, because we don't keep state of what style we're currently
- // in, so we can't reset the size to normal before changing it. Now
- // that we're passing an options parameter we should be able to fix
- // this.
- node.setAttribute(
- "mathsize", buildCommon.sizingMultiplier[group.value.size] + "em");
-
- return node;
-};
-
-groupTypes.overline = function(group, options) {
- var operator = new mathMLTree.MathNode(
- "mo", [new mathMLTree.TextNode("\u203e")]);
- operator.setAttribute("stretchy", "true");
-
- var node = new mathMLTree.MathNode(
- "mover",
- [buildGroup(group.value.body, options),
- operator]);
- node.setAttribute("accent", "true");
-
- return node;
-};
-
-groupTypes.underline = function(group, options) {
- var operator = new mathMLTree.MathNode(
- "mo", [new mathMLTree.TextNode("\u203e")]);
- operator.setAttribute("stretchy", "true");
-
- var node = new mathMLTree.MathNode(
- "munder",
- [buildGroup(group.value.body, options),
- operator]);
- node.setAttribute("accentunder", "true");
-
- return node;
-};
-
-groupTypes.rule = function(group) {
- // TODO(emily): Figure out if there's an actual way to draw black boxes
- // in MathML.
- var node = new mathMLTree.MathNode("mrow");
-
- return node;
-};
-
-groupTypes.kern = function(group) {
- // TODO(kevin): Figure out if there's a way to add space in MathML
- var node = new mathMLTree.MathNode("mrow");
-
- return node;
-};
-
-groupTypes.llap = function(group, options) {
- var node = new mathMLTree.MathNode(
- "mpadded", [buildGroup(group.value.body, options)]);
-
- node.setAttribute("lspace", "-1width");
- node.setAttribute("width", "0px");
-
- return node;
-};
-
-groupTypes.rlap = function(group, options) {
- var node = new mathMLTree.MathNode(
- "mpadded", [buildGroup(group.value.body, options)]);
-
- node.setAttribute("width", "0px");
-
- return node;
-};
-
-groupTypes.phantom = function(group, options, prev) {
- var inner = buildExpression(group.value.value, options);
- return new mathMLTree.MathNode("mphantom", inner);
-};
-
-/**
- * Takes a list of nodes, builds them, and returns a list of the generated
- * MathML nodes. A little simpler than the HTML version because we don't do any
- * previous-node handling.
- */
-var buildExpression = function(expression, options) {
- var groups = [];
- for (var i = 0; i < expression.length; i++) {
- var group = expression[i];
- groups.push(buildGroup(group, options));
- }
- return groups;
-};
-
-/**
- * Takes a group from the parser and calls the appropriate groupTypes function
- * on it to produce a MathML node.
- */
-var buildGroup = function(group, options) {
- if (!group) {
- return new mathMLTree.MathNode("mrow");
- }
-
- if (groupTypes[group.type]) {
- // Call the groupTypes function
- return groupTypes[group.type](group, options);
- } else {
- throw new ParseError(
- "Got group of unknown type: '" + group.type + "'");
- }
-};
-
-/**
- * Takes a full parse tree and settings and builds a MathML representation of
- * it. In particular, we put the elements from building the parse tree into a
- * <semantics> tag so we can also include that TeX source as an annotation.
- *
- * Note that we actually return a domTree element with a `<math>` inside it so
- * we can do appropriate styling.
- */
-var buildMathML = function(tree, texExpression, options) {
- var expression = buildExpression(tree, options);
-
- // Wrap up the expression in an mrow so it is presented in the semantics
- // tag correctly.
- var wrapper = new mathMLTree.MathNode("mrow", expression);
-
- // Build a TeX annotation of the source
- var annotation = new mathMLTree.MathNode(
- "annotation", [new mathMLTree.TextNode(texExpression)]);
-
- annotation.setAttribute("encoding", "application/x-tex");
-
- var semantics = new mathMLTree.MathNode(
- "semantics", [wrapper, annotation]);
-
- var math = new mathMLTree.MathNode("math", [semantics]);
-
- // You can't style <math> nodes, so we wrap the node in a span.
- return makeSpan(["katex-mathml"], [math]);
-};
-
-module.exports = buildMathML;
-
-},{"./ParseError":6,"./buildCommon":10,"./fontMetrics":17,"./mathMLTree":20,"./symbols":23,"./utils":25}],13:[function(require,module,exports){
-var buildHTML = require("./buildHTML");
-var buildMathML = require("./buildMathML");
-var buildCommon = require("./buildCommon");
-var Options = require("./Options");
-var Settings = require("./Settings");
-var Style = require("./Style");
-
-var makeSpan = buildCommon.makeSpan;
-
-var buildTree = function(tree, expression, settings) {
- settings = settings || new Settings({});
-
- var startStyle = Style.TEXT;
- if (settings.displayMode) {
- startStyle = Style.DISPLAY;
- }
-
- // Setup the default options
- var options = new Options({
- style: startStyle,
- size: "size5",
- });
-
- // `buildHTML` sometimes messes with the parse tree (like turning bins ->
- // ords), so we build the MathML version first.
- var mathMLNode = buildMathML(tree, expression, options);
- var htmlNode = buildHTML(tree, options);
-
- var katexNode = makeSpan(["katex"], [
- mathMLNode, htmlNode,
- ]);
-
- if (settings.displayMode) {
- return makeSpan(["katex-display"], [katexNode]);
- } else {
- return katexNode;
- }
-};
-
-module.exports = buildTree;
-
-},{"./Options":5,"./Settings":8,"./Style":9,"./buildCommon":10,"./buildHTML":11,"./buildMathML":12}],14:[function(require,module,exports){
-/**
- * This file deals with creating delimiters of various sizes. The TeXbook
- * discusses these routines on page 441-442, in the "Another subroutine sets box
- * x to a specified variable delimiter" paragraph.
- *
- * There are three main routines here. `makeSmallDelim` makes a delimiter in the
- * normal font, but in either text, script, or scriptscript style.
- * `makeLargeDelim` makes a delimiter in textstyle, but in one of the Size1,
- * Size2, Size3, or Size4 fonts. `makeStackedDelim` makes a delimiter out of
- * smaller pieces that are stacked on top of one another.
- *
- * The functions take a parameter `center`, which determines if the delimiter
- * should be centered around the axis.
- *
- * Then, there are three exposed functions. `sizedDelim` makes a delimiter in
- * one of the given sizes. This is used for things like `\bigl`.
- * `customSizedDelim` makes a delimiter with a given total height+depth. It is
- * called in places like `\sqrt`. `leftRightDelim` makes an appropriate
- * delimiter which surrounds an expression of a given height an depth. It is
- * used in `\left` and `\right`.
- */
-
-var ParseError = require("./ParseError");
-var Style = require("./Style");
-
-var buildCommon = require("./buildCommon");
-var fontMetrics = require("./fontMetrics");
-var symbols = require("./symbols");
-var utils = require("./utils");
-
-var makeSpan = buildCommon.makeSpan;
-
-/**
- * Get the metrics for a given symbol and font, after transformation (i.e.
- * after following replacement from symbols.js)
- */
-var getMetrics = function(symbol, font) {
- if (symbols.math[symbol] && symbols.math[symbol].replace) {
- return fontMetrics.getCharacterMetrics(
- symbols.math[symbol].replace, font);
- } else {
- return fontMetrics.getCharacterMetrics(
- symbol, font);
- }
-};
-
-/**
- * Builds a symbol in the given font size (note size is an integer)
- */
-var mathrmSize = function(value, size, mode) {
- return buildCommon.makeSymbol(value, "Size" + size + "-Regular", mode);
-};
-
-/**
- * Puts a delimiter span in a given style, and adds appropriate height, depth,
- * and maxFontSizes.
- */
-var styleWrap = function(delim, toStyle, options) {
- var span = makeSpan(
- ["style-wrap", options.style.reset(), toStyle.cls()], [delim]);
-
- var multiplier = toStyle.sizeMultiplier / options.style.sizeMultiplier;
-
- span.height *= multiplier;
- span.depth *= multiplier;
- span.maxFontSize = toStyle.sizeMultiplier;
-
- return span;
-};
-
-/**
- * Makes a small delimiter. This is a delimiter that comes in the Main-Regular
- * font, but is restyled to either be in textstyle, scriptstyle, or
- * scriptscriptstyle.
- */
-var makeSmallDelim = function(delim, style, center, options, mode) {
- var text = buildCommon.makeSymbol(delim, "Main-Regular", mode);
-
- var span = styleWrap(text, style, options);
-
- if (center) {
- var shift =
- (1 - options.style.sizeMultiplier / style.sizeMultiplier) *
- fontMetrics.metrics.axisHeight;
-
- span.style.top = shift + "em";
- span.height -= shift;
- span.depth += shift;
- }
-
- return span;
-};
-
-/**
- * Makes a large delimiter. This is a delimiter that comes in the Size1, Size2,
- * Size3, or Size4 fonts. It is always rendered in textstyle.
- */
-var makeLargeDelim = function(delim, size, center, options, mode) {
- var inner = mathrmSize(delim, size, mode);
-
- var span = styleWrap(
- makeSpan(["delimsizing", "size" + size],
- [inner], options.getColor()),
- Style.TEXT, options);
-
- if (center) {
- var shift = (1 - options.style.sizeMultiplier) *
- fontMetrics.metrics.axisHeight;
-
- span.style.top = shift + "em";
- span.height -= shift;
- span.depth += shift;
- }
-
- return span;
-};
-
-/**
- * Make an inner span with the given offset and in the given font. This is used
- * in `makeStackedDelim` to make the stacking pieces for the delimiter.
- */
-var makeInner = function(symbol, font, mode) {
- var sizeClass;
- // Apply the correct CSS class to choose the right font.
- if (font === "Size1-Regular") {
- sizeClass = "delim-size1";
- } else if (font === "Size4-Regular") {
- sizeClass = "delim-size4";
- }
-
- var inner = makeSpan(
- ["delimsizinginner", sizeClass],
- [makeSpan([], [buildCommon.makeSymbol(symbol, font, mode)])]);
-
- // Since this will be passed into `makeVList` in the end, wrap the element
- // in the appropriate tag that VList uses.
- return {type: "elem", elem: inner};
-};
-
-/**
- * Make a stacked delimiter out of a given delimiter, with the total height at
- * least `heightTotal`. This routine is mentioned on page 442 of the TeXbook.
- */
-var makeStackedDelim = function(delim, heightTotal, center, options, mode) {
- // There are four parts, the top, an optional middle, a repeated part, and a
- // bottom.
- var top;
- var middle;
- var repeat;
- var bottom;
- top = repeat = bottom = delim;
- middle = null;
- // Also keep track of what font the delimiters are in
- var font = "Size1-Regular";
-
- // We set the parts and font based on the symbol. Note that we use
- // '\u23d0' instead of '|' and '\u2016' instead of '\\|' for the
- // repeats of the arrows
- if (delim === "\\uparrow") {
- repeat = bottom = "\u23d0";
- } else if (delim === "\\Uparrow") {
- repeat = bottom = "\u2016";
- } else if (delim === "\\downarrow") {
- top = repeat = "\u23d0";
- } else if (delim === "\\Downarrow") {
- top = repeat = "\u2016";
- } else if (delim === "\\updownarrow") {
- top = "\\uparrow";
- repeat = "\u23d0";
- bottom = "\\downarrow";
- } else if (delim === "\\Updownarrow") {
- top = "\\Uparrow";
- repeat = "\u2016";
- bottom = "\\Downarrow";
- } else if (delim === "[" || delim === "\\lbrack") {
- top = "\u23a1";
- repeat = "\u23a2";
- bottom = "\u23a3";
- font = "Size4-Regular";
- } else if (delim === "]" || delim === "\\rbrack") {
- top = "\u23a4";
- repeat = "\u23a5";
- bottom = "\u23a6";
- font = "Size4-Regular";
- } else if (delim === "\\lfloor") {
- repeat = top = "\u23a2";
- bottom = "\u23a3";
- font = "Size4-Regular";
- } else if (delim === "\\lceil") {
- top = "\u23a1";
- repeat = bottom = "\u23a2";
- font = "Size4-Regular";
- } else if (delim === "\\rfloor") {
- repeat = top = "\u23a5";
- bottom = "\u23a6";
- font = "Size4-Regular";
- } else if (delim === "\\rceil") {
- top = "\u23a4";
- repeat = bottom = "\u23a5";
- font = "Size4-Regular";
- } else if (delim === "(") {
- top = "\u239b";
- repeat = "\u239c";
- bottom = "\u239d";
- font = "Size4-Regular";
- } else if (delim === ")") {
- top = "\u239e";
- repeat = "\u239f";
- bottom = "\u23a0";
- font = "Size4-Regular";
- } else if (delim === "\\{" || delim === "\\lbrace") {
- top = "\u23a7";
- middle = "\u23a8";
- bottom = "\u23a9";
- repeat = "\u23aa";
- font = "Size4-Regular";
- } else if (delim === "\\}" || delim === "\\rbrace") {
- top = "\u23ab";
- middle = "\u23ac";
- bottom = "\u23ad";
- repeat = "\u23aa";
- font = "Size4-Regular";
- } else if (delim === "\\lgroup") {
- top = "\u23a7";
- bottom = "\u23a9";
- repeat = "\u23aa";
- font = "Size4-Regular";
- } else if (delim === "\\rgroup") {
- top = "\u23ab";
- bottom = "\u23ad";
- repeat = "\u23aa";
- font = "Size4-Regular";
- } else if (delim === "\\lmoustache") {
- top = "\u23a7";
- bottom = "\u23ad";
- repeat = "\u23aa";
- font = "Size4-Regular";
- } else if (delim === "\\rmoustache") {
- top = "\u23ab";
- bottom = "\u23a9";
- repeat = "\u23aa";
- font = "Size4-Regular";
- } else if (delim === "\\surd") {
- top = "\ue001";
- bottom = "\u23b7";
- repeat = "\ue000";
- font = "Size4-Regular";
- }
-
- // Get the metrics of the four sections
- var topMetrics = getMetrics(top, font);
- var topHeightTotal = topMetrics.height + topMetrics.depth;
- var repeatMetrics = getMetrics(repeat, font);
- var repeatHeightTotal = repeatMetrics.height + repeatMetrics.depth;
- var bottomMetrics = getMetrics(bottom, font);
- var bottomHeightTotal = bottomMetrics.height + bottomMetrics.depth;
- var middleHeightTotal = 0;
- var middleFactor = 1;
- if (middle !== null) {
- var middleMetrics = getMetrics(middle, font);
- middleHeightTotal = middleMetrics.height + middleMetrics.depth;
- middleFactor = 2; // repeat symmetrically above and below middle
- }
-
- // Calcuate the minimal height that the delimiter can have.
- // It is at least the size of the top, bottom, and optional middle combined.
- var minHeight = topHeightTotal + bottomHeightTotal + middleHeightTotal;
-
- // Compute the number of copies of the repeat symbol we will need
- var repeatCount = Math.ceil(
- (heightTotal - minHeight) / (middleFactor * repeatHeightTotal));
-
- // Compute the total height of the delimiter including all the symbols
- var realHeightTotal =
- minHeight + repeatCount * middleFactor * repeatHeightTotal;
-
- // The center of the delimiter is placed at the center of the axis. Note
- // that in this context, "center" means that the delimiter should be
- // centered around the axis in the current style, while normally it is
- // centered around the axis in textstyle.
- var axisHeight = fontMetrics.metrics.axisHeight;
- if (center) {
- axisHeight *= options.style.sizeMultiplier;
- }
- // Calculate the depth
- var depth = realHeightTotal / 2 - axisHeight;
-
- // Now, we start building the pieces that will go into the vlist
-
- // Keep a list of the inner pieces
- var inners = [];
-
- // Add the bottom symbol
- inners.push(makeInner(bottom, font, mode));
-
- var i;
- if (middle === null) {
- // Add that many symbols
- for (i = 0; i < repeatCount; i++) {
- inners.push(makeInner(repeat, font, mode));
- }
- } else {
- // When there is a middle bit, we need the middle part and two repeated
- // sections
- for (i = 0; i < repeatCount; i++) {
- inners.push(makeInner(repeat, font, mode));
- }
- inners.push(makeInner(middle, font, mode));
- for (i = 0; i < repeatCount; i++) {
- inners.push(makeInner(repeat, font, mode));
- }
- }
-
- // Add the top symbol
- inners.push(makeInner(top, font, mode));
-
- // Finally, build the vlist
- var inner = buildCommon.makeVList(inners, "bottom", depth, options);
-
- return styleWrap(
- makeSpan(["delimsizing", "mult"], [inner], options.getColor()),
- Style.TEXT, options);
-};
-
-// There are three kinds of delimiters, delimiters that stack when they become
-// too large
-var stackLargeDelimiters = [
- "(", ")", "[", "\\lbrack", "]", "\\rbrack",
- "\\{", "\\lbrace", "\\}", "\\rbrace",
- "\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
- "\\surd",
-];
-
-// delimiters that always stack
-var stackAlwaysDelimiters = [
- "\\uparrow", "\\downarrow", "\\updownarrow",
- "\\Uparrow", "\\Downarrow", "\\Updownarrow",
- "|", "\\|", "\\vert", "\\Vert",
- "\\lvert", "\\rvert", "\\lVert", "\\rVert",
- "\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache",
-];
-
-// and delimiters that never stack
-var stackNeverDelimiters = [
- "<", ">", "\\langle", "\\rangle", "/", "\\backslash", "\\lt", "\\gt",
-];
-
-// Metrics of the different sizes. Found by looking at TeX's output of
-// $\bigl| // \Bigl| \biggl| \Biggl| \showlists$
-// Used to create stacked delimiters of appropriate sizes in makeSizedDelim.
-var sizeToMaxHeight = [0, 1.2, 1.8, 2.4, 3.0];
-
-/**
- * Used to create a delimiter of a specific size, where `size` is 1, 2, 3, or 4.
- */
-var makeSizedDelim = function(delim, size, options, mode) {
- // < and > turn into \langle and \rangle in delimiters
- if (delim === "<" || delim === "\\lt") {
- delim = "\\langle";
- } else if (delim === ">" || delim === "\\gt") {
- delim = "\\rangle";
- }
-
- // Sized delimiters are never centered.
- if (utils.contains(stackLargeDelimiters, delim) ||
- utils.contains(stackNeverDelimiters, delim)) {
- return makeLargeDelim(delim, size, false, options, mode);
- } else if (utils.contains(stackAlwaysDelimiters, delim)) {
- return makeStackedDelim(
- delim, sizeToMaxHeight[size], false, options, mode);
- } else {
- throw new ParseError("Illegal delimiter: '" + delim + "'");
- }
-};
-
-/**
- * There are three different sequences of delimiter sizes that the delimiters
- * follow depending on the kind of delimiter. This is used when creating custom
- * sized delimiters to decide whether to create a small, large, or stacked
- * delimiter.
- *
- * In real TeX, these sequences aren't explicitly defined, but are instead
- * defined inside the font metrics. Since there are only three sequences that
- * are possible for the delimiters that TeX defines, it is easier to just encode
- * them explicitly here.
- */
-
-// Delimiters that never stack try small delimiters and large delimiters only
-var stackNeverDelimiterSequence = [
- {type: "small", style: Style.SCRIPTSCRIPT},
- {type: "small", style: Style.SCRIPT},
- {type: "small", style: Style.TEXT},
- {type: "large", size: 1},
- {type: "large", size: 2},
- {type: "large", size: 3},
- {type: "large", size: 4},
-];
-
-// Delimiters that always stack try the small delimiters first, then stack
-var stackAlwaysDelimiterSequence = [
- {type: "small", style: Style.SCRIPTSCRIPT},
- {type: "small", style: Style.SCRIPT},
- {type: "small", style: Style.TEXT},
- {type: "stack"},
-];
-
-// Delimiters that stack when large try the small and then large delimiters, and
-// stack afterwards
-var stackLargeDelimiterSequence = [
- {type: "small", style: Style.SCRIPTSCRIPT},
- {type: "small", style: Style.SCRIPT},
- {type: "small", style: Style.TEXT},
- {type: "large", size: 1},
- {type: "large", size: 2},
- {type: "large", size: 3},
- {type: "large", size: 4},
- {type: "stack"},
-];
-
-/**
- * Get the font used in a delimiter based on what kind of delimiter it is.
- */
-var delimTypeToFont = function(type) {
- if (type.type === "small") {
- return "Main-Regular";
- } else if (type.type === "large") {
- return "Size" + type.size + "-Regular";
- } else if (type.type === "stack") {
- return "Size4-Regular";
- }
-};
-
-/**
- * Traverse a sequence of types of delimiters to decide what kind of delimiter
- * should be used to create a delimiter of the given height+depth.
- */
-var traverseSequence = function(delim, height, sequence, options) {
- // Here, we choose the index we should start at in the sequences. In smaller
- // sizes (which correspond to larger numbers in style.size) we start earlier
- // in the sequence. Thus, scriptscript starts at index 3-3=0, script starts
- // at index 3-2=1, text starts at 3-1=2, and display starts at min(2,3-0)=2
- var start = Math.min(2, 3 - options.style.size);
- for (var i = start; i < sequence.length; i++) {
- if (sequence[i].type === "stack") {
- // This is always the last delimiter, so we just break the loop now.
- break;
- }
-
- var metrics = getMetrics(delim, delimTypeToFont(sequence[i]));
- var heightDepth = metrics.height + metrics.depth;
-
- // Small delimiters are scaled down versions of the same font, so we
- // account for the style change size.
-
- if (sequence[i].type === "small") {
- heightDepth *= sequence[i].style.sizeMultiplier;
- }
-
- // Check if the delimiter at this size works for the given height.
- if (heightDepth > height) {
- return sequence[i];
- }
- }
-
- // If we reached the end of the sequence, return the last sequence element.
- return sequence[sequence.length - 1];
-};
-
-/**
- * Make a delimiter of a given height+depth, with optional centering. Here, we
- * traverse the sequences, and create a delimiter that the sequence tells us to.
- */
-var makeCustomSizedDelim = function(delim, height, center, options, mode) {
- if (delim === "<" || delim === "\\lt") {
- delim = "\\langle";
- } else if (delim === ">" || delim === "\\gt") {
- delim = "\\rangle";
- }
-
- // Decide what sequence to use
- var sequence;
- if (utils.contains(stackNeverDelimiters, delim)) {
- sequence = stackNeverDelimiterSequence;
- } else if (utils.contains(stackLargeDelimiters, delim)) {
- sequence = stackLargeDelimiterSequence;
- } else {
- sequence = stackAlwaysDelimiterSequence;
- }
-
- // Look through the sequence
- var delimType = traverseSequence(delim, height, sequence, options);
-
- // Depending on the sequence element we decided on, call the appropriate
- // function.
- if (delimType.type === "small") {
- return makeSmallDelim(delim, delimType.style, center, options, mode);
- } else if (delimType.type === "large") {
- return makeLargeDelim(delim, delimType.size, center, options, mode);
- } else if (delimType.type === "stack") {
- return makeStackedDelim(delim, height, center, options, mode);
- }
-};
-
-/**
- * Make a delimiter for use with `\left` and `\right`, given a height and depth
- * of an expression that the delimiters surround.
- */
-var makeLeftRightDelim = function(delim, height, depth, options, mode) {
- // We always center \left/\right delimiters, so the axis is always shifted
- var axisHeight =
- fontMetrics.metrics.axisHeight * options.style.sizeMultiplier;
-
- // Taken from TeX source, tex.web, function make_left_right
- var delimiterFactor = 901;
- var delimiterExtend = 5.0 / fontMetrics.metrics.ptPerEm;
-
- var maxDistFromAxis = Math.max(
- height - axisHeight, depth + axisHeight);
-
- var totalHeight = Math.max(
- // In real TeX, calculations are done using integral values which are
- // 65536 per pt, or 655360 per em. So, the division here truncates in
- // TeX but doesn't here, producing different results. If we wanted to
- // exactly match TeX's calculation, we could do
- // Math.floor(655360 * maxDistFromAxis / 500) *
- // delimiterFactor / 655360
- // (To see the difference, compare
- // x^{x^{\left(\rule{0.1em}{0.68em}\right)}}
- // in TeX and KaTeX)
- maxDistFromAxis / 500 * delimiterFactor,
- 2 * maxDistFromAxis - delimiterExtend);
-
- // Finally, we defer to `makeCustomSizedDelim` with our calculated total
- // height
- return makeCustomSizedDelim(delim, totalHeight, true, options, mode);
-};
-
-module.exports = {
- sizedDelim: makeSizedDelim,
- customSizedDelim: makeCustomSizedDelim,
- leftRightDelim: makeLeftRightDelim,
-};
-
-},{"./ParseError":6,"./Style":9,"./buildCommon":10,"./fontMetrics":17,"./symbols":23,"./utils":25}],15:[function(require,module,exports){
-/**
- * These objects store the data about the DOM nodes we create, as well as some
- * extra data. They can then be transformed into real DOM nodes with the
- * `toNode` function or HTML markup using `toMarkup`. They are useful for both
- * storing extra properties on the nodes, as well as providing a way to easily
- * work with the DOM.
- *
- * Similar functions for working with MathML nodes exist in mathMLTree.js.
- */
-var unicodeRegexes = require("./unicodeRegexes");
-var utils = require("./utils");
-
-/**
- * Create an HTML className based on a list of classes. In addition to joining
- * with spaces, we also remove null or empty classes.
- */
-var createClass = function(classes) {
- classes = classes.slice();
- for (var i = classes.length - 1; i >= 0; i--) {
- if (!classes[i]) {
- classes.splice(i, 1);
- }
- }
-
- return classes.join(" ");
-};
-
-/**
- * This node represents a span node, with a className, a list of children, and
- * an inline style. It also contains information about its height, depth, and
- * maxFontSize.
- */
-function span(classes, children, height, depth, maxFontSize, style) {
- this.classes = classes || [];
- this.children = children || [];
- this.height = height || 0;
- this.depth = depth || 0;
- this.maxFontSize = maxFontSize || 0;
- this.style = style || {};
- this.attributes = {};
-}
-
-/**
- * Sets an arbitrary attribute on the span. Warning: use this wisely. Not all
- * browsers support attributes the same, and having too many custom attributes
- * is probably bad.
- */
-span.prototype.setAttribute = function(attribute, value) {
- this.attributes[attribute] = value;
-};
-
-/**
- * Convert the span into an HTML node
- */
-span.prototype.toNode = function() {
- var span = document.createElement("span");
-
- // Apply the class
- span.className = createClass(this.classes);
-
- // Apply inline styles
- for (var style in this.style) {
- if (Object.prototype.hasOwnProperty.call(this.style, style)) {
- span.style[style] = this.style[style];
- }
- }
-
- // Apply attributes
- for (var attr in this.attributes) {
- if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
- span.setAttribute(attr, this.attributes[attr]);
- }
- }
-
- // Append the children, also as HTML nodes
- for (var i = 0; i < this.children.length; i++) {
- span.appendChild(this.children[i].toNode());
- }
-
- return span;
-};
-
-/**
- * Convert the span into an HTML markup string
- */
-span.prototype.toMarkup = function() {
- var markup = "<span";
-
- // Add the class
- if (this.classes.length) {
- markup += " class=\"";
- markup += utils.escape(createClass(this.classes));
- markup += "\"";
- }
-
- var styles = "";
-
- // Add the styles, after hyphenation
- for (var style in this.style) {
- if (this.style.hasOwnProperty(style)) {
- styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
- }
- }
-
- if (styles) {
- markup += " style=\"" + utils.escape(styles) + "\"";
- }
-
- // Add the attributes
- for (var attr in this.attributes) {
- if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
- markup += " " + attr + "=\"";
- markup += utils.escape(this.attributes[attr]);
- markup += "\"";
- }
- }
-
- markup += ">";
-
- // Add the markup of the children, also as markup
- for (var i = 0; i < this.children.length; i++) {
- markup += this.children[i].toMarkup();
- }
-
- markup += "</span>";
-
- return markup;
-};
-
-/**
- * This node represents a document fragment, which contains elements, but when
- * placed into the DOM doesn't have any representation itself. Thus, it only
- * contains children and doesn't have any HTML properties. It also keeps track
- * of a height, depth, and maxFontSize.
- */
-function documentFragment(children, height, depth, maxFontSize) {
- this.children = children || [];
- this.height = height || 0;
- this.depth = depth || 0;
- this.maxFontSize = maxFontSize || 0;
-}
-
-/**
- * Convert the fragment into a node
- */
-documentFragment.prototype.toNode = function() {
- // Create a fragment
- var frag = document.createDocumentFragment();
-
- // Append the children
- for (var i = 0; i < this.children.length; i++) {
- frag.appendChild(this.children[i].toNode());
- }
-
- return frag;
-};
-
-/**
- * Convert the fragment into HTML markup
- */
-documentFragment.prototype.toMarkup = function() {
- var markup = "";
-
- // Simply concatenate the markup for the children together
- for (var i = 0; i < this.children.length; i++) {
- markup += this.children[i].toMarkup();
- }
-
- return markup;
-};
-
-var iCombinations = {
- 'î': '\u0131\u0302',
- 'ï': '\u0131\u0308',
- 'í': '\u0131\u0301',
- // 'Ä«': '\u0131\u0304', // enable when we add Extended Latin
- 'ì': '\u0131\u0300',
-};
-
-/**
- * A symbol node contains information about a single symbol. It either renders
- * to a single text node, or a span with a single text node in it, depending on
- * whether it has CSS classes, styles, or needs italic correction.
- */
-function symbolNode(value, height, depth, italic, skew, classes, style) {
- this.value = value || "";
- this.height = height || 0;
- this.depth = depth || 0;
- this.italic = italic || 0;
- this.skew = skew || 0;
- this.classes = classes || [];
- this.style = style || {};
- this.maxFontSize = 0;
-
- // Mark CJK characters with specific classes so that we can specify which
- // fonts to use. This allows us to render these characters with a serif
- // font in situations where the browser would either default to a sans serif
- // or render a placeholder character.
- if (unicodeRegexes.cjkRegex.test(value)) {
- // I couldn't find any fonts that contained Hangul as well as all of
- // the other characters we wanted to test there for it gets its own
- // CSS class.
- if (unicodeRegexes.hangulRegex.test(value)) {
- this.classes.push('hangul_fallback');
- } else {
- this.classes.push('cjk_fallback');
- }
- }
-
- if (/[îïíì]/.test(this.value)) { // add ī when we add Extended Latin
- this.value = iCombinations[this.value];
- }
-}
-
-/**
- * Creates a text node or span from a symbol node. Note that a span is only
- * created if it is needed.
- */
-symbolNode.prototype.toNode = function() {
- var node = document.createTextNode(this.value);
- var span = null;
-
- if (this.italic > 0) {
- span = document.createElement("span");
- span.style.marginRight = this.italic + "em";
- }
-
- if (this.classes.length > 0) {
- span = span || document.createElement("span");
- span.className = createClass(this.classes);
- }
-
- for (var style in this.style) {
- if (this.style.hasOwnProperty(style)) {
- span = span || document.createElement("span");
- span.style[style] = this.style[style];
- }
- }
-
- if (span) {
- span.appendChild(node);
- return span;
- } else {
- return node;
- }
-};
-
-/**
- * Creates markup for a symbol node.
- */
-symbolNode.prototype.toMarkup = function() {
- // TODO(alpert): More duplication than I'd like from
- // span.prototype.toMarkup and symbolNode.prototype.toNode...
- var needsSpan = false;
-
- var markup = "<span";
-
- if (this.classes.length) {
- needsSpan = true;
- markup += " class=\"";
- markup += utils.escape(createClass(this.classes));
- markup += "\"";
- }
-
- var styles = "";
-
- if (this.italic > 0) {
- styles += "margin-right:" + this.italic + "em;";
- }
- for (var style in this.style) {
- if (this.style.hasOwnProperty(style)) {
- styles += utils.hyphenate(style) + ":" + this.style[style] + ";";
- }
- }
-
- if (styles) {
- needsSpan = true;
- markup += " style=\"" + utils.escape(styles) + "\"";
- }
-
- var escaped = utils.escape(this.value);
- if (needsSpan) {
- markup += ">";
- markup += escaped;
- markup += "</span>";
- return markup;
- } else {
- return escaped;
- }
-};
-
-module.exports = {
- span: span,
- documentFragment: documentFragment,
- symbolNode: symbolNode,
-};
-
-},{"./unicodeRegexes":24,"./utils":25}],16:[function(require,module,exports){
-/* eslint no-constant-condition:0 */
-var fontMetrics = require("./fontMetrics");
-var parseData = require("./parseData");
-var ParseError = require("./ParseError");
-
-var ParseNode = parseData.ParseNode;
-
-/**
- * Parse the body of the environment, with rows delimited by \\ and
- * columns delimited by &, and create a nested list in row-major order
- * with one group per cell.
- */
-function parseArray(parser, result) {
- var row = [];
- var body = [row];
- var rowGaps = [];
- while (true) {
- var cell = parser.parseExpression(false, null);
- row.push(new ParseNode("ordgroup", cell, parser.mode));
- var next = parser.nextToken.text;
- if (next === "&") {
- parser.consume();
- } else if (next === "\\end") {
- break;
- } else if (next === "\\\\" || next === "\\cr") {
- var cr = parser.parseFunction();
- rowGaps.push(cr.value.size);
- row = [];
- body.push(row);
- } else {
- throw new ParseError("Expected & or \\\\ or \\end",
- parser.nextToken);
- }
- }
- result.body = body;
- result.rowGaps = rowGaps;
- return new ParseNode(result.type, result, parser.mode);
-}
-
-/*
- * An environment definition is very similar to a function definition:
- * it is declared with a name or a list of names, a set of properties
- * and a handler containing the actual implementation.
- *
- * The properties include:
- * - numArgs: The number of arguments after the \begin{name} function.
- * - argTypes: (optional) Just like for a function
- * - allowedInText: (optional) Whether or not the environment is allowed inside
- * text mode (default false) (not enforced yet)
- * - numOptionalArgs: (optional) Just like for a function
- * A bare number instead of that object indicates the numArgs value.
- *
- * The handler function will receive two arguments
- * - context: information and references provided by the parser
- * - args: an array of arguments passed to \begin{name}
- * The context contains the following properties:
- * - envName: the name of the environment, one of the listed names.
- * - parser: the parser object
- * - lexer: the lexer object
- * - positions: the positions associated with these arguments from args.
- * The handler must return a ParseResult.
- */
-
-function defineEnvironment(names, props, handler) {
- if (typeof names === "string") {
- names = [names];
- }
- if (typeof props === "number") {
- props = { numArgs: props };
- }
- // Set default values of environments
- var data = {
- numArgs: props.numArgs || 0,
- argTypes: props.argTypes,
- greediness: 1,
- allowedInText: !!props.allowedInText,
- numOptionalArgs: props.numOptionalArgs || 0,
- handler: handler,
- };
- for (var i = 0; i < names.length; ++i) {
- module.exports[names[i]] = data;
- }
-}
-
-// Arrays are part of LaTeX, defined in lttab.dtx so its documentation
-// is part of the source2e.pdf file of LaTeX2e source documentation.
-defineEnvironment("array", {
- numArgs: 1,
-}, function(context, args) {
- var colalign = args[0];
- colalign = colalign.value.map ? colalign.value : [colalign];
- var cols = colalign.map(function(node) {
- var ca = node.value;
- if ("lcr".indexOf(ca) !== -1) {
- return {
- type: "align",
- align: ca,
- };
- } else if (ca === "|") {
- return {
- type: "separator",
- separator: "|",
- };
- }
- throw new ParseError(
- "Unknown column alignment: " + node.value,
- node);
- });
- var res = {
- type: "array",
- cols: cols,
- hskipBeforeAndAfter: true, // \@preamble in lttab.dtx
- };
- res = parseArray(context.parser, res);
- return res;
-});
-
-// The matrix environments of amsmath builds on the array environment
-// of LaTeX, which is discussed above.
-defineEnvironment([
- "matrix",
- "pmatrix",
- "bmatrix",
- "Bmatrix",
- "vmatrix",
- "Vmatrix",
-], {
-}, function(context) {
- var delimiters = {
- "matrix": null,
- "pmatrix": ["(", ")"],
- "bmatrix": ["[", "]"],
- "Bmatrix": ["\\{", "\\}"],
- "vmatrix": ["|", "|"],
- "Vmatrix": ["\\Vert", "\\Vert"],
- }[context.envName];
- var res = {
- type: "array",
- hskipBeforeAndAfter: false, // \hskip -\arraycolsep in amsmath
- };
- res = parseArray(context.parser, res);
- if (delimiters) {
- res = new ParseNode("leftright", {
- body: [res],
- left: delimiters[0],
- right: delimiters[1],
- }, context.mode);
- }
- return res;
-});
-
-// A cases environment (in amsmath.sty) is almost equivalent to
-// \def\arraystretch{1.2}%
-// \left\{\begin{array}{@{}l@{\quad}l@{}} … \end{array}\right.
-defineEnvironment("cases", {
-}, function(context) {
- var res = {
- type: "array",
- arraystretch: 1.2,
- cols: [{
- type: "align",
- align: "l",
- pregap: 0,
- postgap: fontMetrics.metrics.quad,
- }, {
- type: "align",
- align: "l",
- pregap: 0,
- postgap: 0,
- }],
- };
- res = parseArray(context.parser, res);
- res = new ParseNode("leftright", {
- body: [res],
- left: "\\{",
- right: ".",
- }, context.mode);
- return res;
-});
-
-// An aligned environment is like the align* environment
-// except it operates within math mode.
-// Note that we assume \nomallineskiplimit to be zero,
-// so that \strut@ is the same as \strut.
-defineEnvironment("aligned", {
-}, function(context) {
- var res = {
- type: "array",
- cols: [],
- };
- res = parseArray(context.parser, res);
- var emptyGroup = new ParseNode("ordgroup", [], context.mode);
- var numCols = 0;
- res.value.body.forEach(function(row) {
- var i;
- for (i = 1; i < row.length; i += 2) {
- row[i].value.unshift(emptyGroup);
- }
- if (numCols < row.length) {
- numCols = row.length;
- }
- });
- for (var i = 0; i < numCols; ++i) {
- var align = "r";
- var pregap = 0;
- if (i % 2 === 1) {
- align = "l";
- } else if (i > 0) {
- pregap = 2; // one \qquad between columns
- }
- res.value.cols[i] = {
- type: "align",
- align: align,
- pregap: pregap,
- postgap: 0,
- };
- }
- return res;
-});
-
-},{"./ParseError":6,"./fontMetrics":17,"./parseData":21}],17:[function(require,module,exports){
-/* eslint no-unused-vars:0 */
-
-var Style = require("./Style");
-var cjkRegex = require("./unicodeRegexes").cjkRegex;
-
-/**
- * This file contains metrics regarding fonts and individual symbols. The sigma
- * and xi variables, as well as the metricMap map contain data extracted from
- * TeX, TeX font metrics, and the TTF files. These data are then exposed via the
- * `metrics` variable and the getCharacterMetrics function.
- */
-
-// These font metrics are extracted from TeX by using
-// \font\a=cmmi10
-// \showthe\fontdimenX\a
-// where X is the corresponding variable number. These correspond to the font
-// parameters of the symbol fonts. In TeX, there are actually three sets of
-// dimensions, one for each of textstyle, scriptstyle, and scriptscriptstyle,
-// but we only use the textstyle ones, and scale certain dimensions accordingly.
-// See the TeXbook, page 441.
-var sigma1 = 0.025;
-var sigma2 = 0;
-var sigma3 = 0;
-var sigma4 = 0;
-var sigma5 = 0.431;
-var sigma6 = 1;
-var sigma7 = 0;
-var sigma8 = 0.677;
-var sigma9 = 0.394;
-var sigma10 = 0.444;
-var sigma11 = 0.686;
-var sigma12 = 0.345;
-var sigma13 = 0.413;
-var sigma14 = 0.363;
-var sigma15 = 0.289;
-var sigma16 = 0.150;
-var sigma17 = 0.247;
-var sigma18 = 0.386;
-var sigma19 = 0.050;
-var sigma20 = 2.390;
-var sigma21 = 1.01;
-var sigma21Script = 0.81;
-var sigma21ScriptScript = 0.71;
-var sigma22 = 0.250;
-
-// These font metrics are extracted from TeX by using
-// \font\a=cmex10
-// \showthe\fontdimenX\a
-// where X is the corresponding variable number. These correspond to the font
-// parameters of the extension fonts (family 3). See the TeXbook, page 441.
-var xi1 = 0;
-var xi2 = 0;
-var xi3 = 0;
-var xi4 = 0;
-var xi5 = 0.431;
-var xi6 = 1;
-var xi7 = 0;
-var xi8 = 0.04;
-var xi9 = 0.111;
-var xi10 = 0.166;
-var xi11 = 0.2;
-var xi12 = 0.6;
-var xi13 = 0.1;
-
-// This value determines how large a pt is, for metrics which are defined in
-// terms of pts.
-// This value is also used in katex.less; if you change it make sure the values
-// match.
-var ptPerEm = 10.0;
-
-// The space between adjacent `|` columns in an array definition. From
-// `\showthe\doublerulesep` in LaTeX.
-var doubleRuleSep = 2.0 / ptPerEm;
-
-/**
- * This is just a mapping from common names to real metrics
- */
-var metrics = {
- xHeight: sigma5,
- quad: sigma6,
- num1: sigma8,
- num2: sigma9,
- num3: sigma10,
- denom1: sigma11,
- denom2: sigma12,
- sup1: sigma13,
- sup2: sigma14,
- sup3: sigma15,
- sub1: sigma16,
- sub2: sigma17,
- supDrop: sigma18,
- subDrop: sigma19,
- axisHeight: sigma22,
- defaultRuleThickness: xi8,
- bigOpSpacing1: xi9,
- bigOpSpacing2: xi10,
- bigOpSpacing3: xi11,
- bigOpSpacing4: xi12,
- bigOpSpacing5: xi13,
- ptPerEm: ptPerEm,
- emPerEx: sigma5 / sigma6,
- doubleRuleSep: doubleRuleSep,
-
- // TODO(alpert): Missing parallel structure here. We should probably add
- // style-specific metrics for all of these.
- delim1: sigma20,
- getDelim2: function(style) {
- if (style.size === Style.TEXT.size) {
- return sigma21;
- } else if (style.size === Style.SCRIPT.size) {
- return sigma21Script;
- } else if (style.size === Style.SCRIPTSCRIPT.size) {
- return sigma21ScriptScript;
- }
- throw new Error("Unexpected style size: " + style.size);
- },
-};
-
-// This map contains a mapping from font name and character code to character
-// metrics, including height, depth, italic correction, and skew (kern from the
-// character to the corresponding \skewchar)
-// This map is generated via `make metrics`. It should not be changed manually.
-var metricMap = require("./fontMetricsData");
-
-// These are very rough approximations. We default to Times New Roman which
-// should have Latin-1 and Cyrillic characters, but may not depending on the
-// operating system. The metrics do not account for extra height from the
-// accents. In the case of Cyrillic characters which have both ascenders and
-// descenders we prefer approximations with ascenders, primarily to prevent
-// the fraction bar or root line from intersecting the glyph.
-// TODO(kevinb) allow union of multiple glyph metrics for better accuracy.
-var extraCharacterMap = {
- // Latin-1
- 'À': 'A',
- 'Ã': 'A',
- 'Â': 'A',
- 'Ã': 'A',
- 'Ä': 'A',
- 'Ã…': 'A',
- 'Æ': 'A',
- 'Ç': 'C',
- 'È': 'E',
- 'É': 'E',
- 'Ê': 'E',
- 'Ë': 'E',
- 'Ì': 'I',
- 'Ã': 'I',
- 'ÃŽ': 'I',
- 'Ã': 'I',
- 'Ã': 'D',
- 'Ñ': 'N',
- 'Ã’': 'O',
- 'Ó': 'O',
- 'Ô': 'O',
- 'Õ': 'O',
- 'Ö': 'O',
- 'Ø': 'O',
- 'Ù': 'U',
- 'Ú': 'U',
- 'Û': 'U',
- 'Ü': 'U',
- 'Ã': 'Y',
- 'Þ': 'o',
- 'ß': 'B',
- 'à': 'a',
- 'á': 'a',
- 'â': 'a',
- 'ã': 'a',
- 'ä': 'a',
- 'Ã¥': 'a',
- 'æ': 'a',
- 'ç': 'c',
- 'è': 'e',
- 'é': 'e',
- 'ê': 'e',
- 'ë': 'e',
- 'ì': 'i',
- 'í': 'i',
- 'î': 'i',
- 'ï': 'i',
- 'ð': 'd',
- 'ñ': 'n',
- 'ò': 'o',
- 'ó': 'o',
- 'ô': 'o',
- 'õ': 'o',
- 'ö': 'o',
- 'ø': 'o',
- 'ù': 'u',
- 'ú': 'u',
- 'û': 'u',
- 'ü': 'u',
- 'ý': 'y',
- 'þ': 'o',
- 'ÿ': 'y',
-
- // Cyrillic
- 'Ð': 'A',
- 'Б': 'B',
- 'Ð’': 'B',
- 'Г': 'F',
- 'Д': 'A',
- 'Е': 'E',
- 'Ж': 'K',
- 'З': '3',
- 'И': 'N',
- 'Й': 'N',
- 'К': 'K',
- 'Л': 'N',
- 'М': 'M',
- 'Ð': 'H',
- 'О': 'O',
- 'П': 'N',
- 'Р': 'P',
- 'С': 'C',
- 'Т': 'T',
- 'У': 'y',
- 'Ф': 'O',
- 'Ð¥': 'X',
- 'Ц': 'U',
- 'Ч': 'h',
- 'Ш': 'W',
- 'Щ': 'W',
- 'Ъ': 'B',
- 'Ы': 'X',
- 'Ь': 'B',
- 'Э': '3',
- 'Ю': 'X',
- 'Я': 'R',
- 'а': 'a',
- 'б': 'b',
- 'в': 'a',
- 'г': 'r',
- 'д': 'y',
- 'е': 'e',
- 'ж': 'm',
- 'з': 'e',
- 'и': 'n',
- 'й': 'n',
- 'к': 'n',
- 'л': 'n',
- 'м': 'm',
- 'н': 'n',
- 'о': 'o',
- 'п': 'n',
- 'Ñ€': 'p',
- 'Ñ': 'c',
- 'Ñ‚': 'o',
- 'у': 'y',
- 'Ñ„': 'b',
- 'Ñ…': 'x',
- 'ц': 'n',
- 'ч': 'n',
- 'ш': 'w',
- 'щ': 'w',
- 'ÑŠ': 'a',
- 'Ñ‹': 'm',
- 'ь': 'a',
- 'Ñ': 'e',
- 'ÑŽ': 'm',
- 'Ñ': 'r',
-};
-
-/**
- * This function is a convenience function for looking up information in the
- * metricMap table. It takes a character as a string, and a style.
- *
- * Note: the `width` property may be undefined if fontMetricsData.js wasn't
- * built using `Make extended_metrics`.
- */
-var getCharacterMetrics = function(character, style) {
- var ch = character.charCodeAt(0);
- if (character[0] in extraCharacterMap) {
- ch = extraCharacterMap[character[0]].charCodeAt(0);
- } else if (cjkRegex.test(character[0])) {
- ch = 'M'.charCodeAt(0);
- }
- var metrics = metricMap[style][ch];
- if (metrics) {
- return {
- depth: metrics[0],
- height: metrics[1],
- italic: metrics[2],
- skew: metrics[3],
- width: metrics[4],
- };
- }
-};
-
-module.exports = {
- metrics: metrics,
- getCharacterMetrics: getCharacterMetrics,
-};
-
-},{"./Style":9,"./fontMetricsData":18,"./unicodeRegexes":24}],18:[function(require,module,exports){
-module.exports = {
- "AMS-Regular": {
- "65": [0, 0.68889, 0, 0],
- "66": [0, 0.68889, 0, 0],
- "67": [0, 0.68889, 0, 0],
- "68": [0, 0.68889, 0, 0],
- "69": [0, 0.68889, 0, 0],
- "70": [0, 0.68889, 0, 0],
- "71": [0, 0.68889, 0, 0],
- "72": [0, 0.68889, 0, 0],
- "73": [0, 0.68889, 0, 0],
- "74": [0.16667, 0.68889, 0, 0],
- "75": [0, 0.68889, 0, 0],
- "76": [0, 0.68889, 0, 0],
- "77": [0, 0.68889, 0, 0],
- "78": [0, 0.68889, 0, 0],
- "79": [0.16667, 0.68889, 0, 0],
- "80": [0, 0.68889, 0, 0],
- "81": [0.16667, 0.68889, 0, 0],
- "82": [0, 0.68889, 0, 0],
- "83": [0, 0.68889, 0, 0],
- "84": [0, 0.68889, 0, 0],
- "85": [0, 0.68889, 0, 0],
- "86": [0, 0.68889, 0, 0],
- "87": [0, 0.68889, 0, 0],
- "88": [0, 0.68889, 0, 0],
- "89": [0, 0.68889, 0, 0],
- "90": [0, 0.68889, 0, 0],
- "107": [0, 0.68889, 0, 0],
- "165": [0, 0.675, 0.025, 0],
- "174": [0.15559, 0.69224, 0, 0],
- "240": [0, 0.68889, 0, 0],
- "295": [0, 0.68889, 0, 0],
- "710": [0, 0.825, 0, 0],
- "732": [0, 0.9, 0, 0],
- "770": [0, 0.825, 0, 0],
- "771": [0, 0.9, 0, 0],
- "989": [0.08167, 0.58167, 0, 0],
- "1008": [0, 0.43056, 0.04028, 0],
- "8245": [0, 0.54986, 0, 0],
- "8463": [0, 0.68889, 0, 0],
- "8487": [0, 0.68889, 0, 0],
- "8498": [0, 0.68889, 0, 0],
- "8502": [0, 0.68889, 0, 0],
- "8503": [0, 0.68889, 0, 0],
- "8504": [0, 0.68889, 0, 0],
- "8513": [0, 0.68889, 0, 0],
- "8592": [-0.03598, 0.46402, 0, 0],
- "8594": [-0.03598, 0.46402, 0, 0],
- "8602": [-0.13313, 0.36687, 0, 0],
- "8603": [-0.13313, 0.36687, 0, 0],
- "8606": [0.01354, 0.52239, 0, 0],
- "8608": [0.01354, 0.52239, 0, 0],
- "8610": [0.01354, 0.52239, 0, 0],
- "8611": [0.01354, 0.52239, 0, 0],
- "8619": [0, 0.54986, 0, 0],
- "8620": [0, 0.54986, 0, 0],
- "8621": [-0.13313, 0.37788, 0, 0],
- "8622": [-0.13313, 0.36687, 0, 0],
- "8624": [0, 0.69224, 0, 0],
- "8625": [0, 0.69224, 0, 0],
- "8630": [0, 0.43056, 0, 0],
- "8631": [0, 0.43056, 0, 0],
- "8634": [0.08198, 0.58198, 0, 0],
- "8635": [0.08198, 0.58198, 0, 0],
- "8638": [0.19444, 0.69224, 0, 0],
- "8639": [0.19444, 0.69224, 0, 0],
- "8642": [0.19444, 0.69224, 0, 0],
- "8643": [0.19444, 0.69224, 0, 0],
- "8644": [0.1808, 0.675, 0, 0],
- "8646": [0.1808, 0.675, 0, 0],
- "8647": [0.1808, 0.675, 0, 0],
- "8648": [0.19444, 0.69224, 0, 0],
- "8649": [0.1808, 0.675, 0, 0],
- "8650": [0.19444, 0.69224, 0, 0],
- "8651": [0.01354, 0.52239, 0, 0],
- "8652": [0.01354, 0.52239, 0, 0],
- "8653": [-0.13313, 0.36687, 0, 0],
- "8654": [-0.13313, 0.36687, 0, 0],
- "8655": [-0.13313, 0.36687, 0, 0],
- "8666": [0.13667, 0.63667, 0, 0],
- "8667": [0.13667, 0.63667, 0, 0],
- "8669": [-0.13313, 0.37788, 0, 0],
- "8672": [-0.064, 0.437, 0, 0],
- "8674": [-0.064, 0.437, 0, 0],
- "8705": [0, 0.825, 0, 0],
- "8708": [0, 0.68889, 0, 0],
- "8709": [0.08167, 0.58167, 0, 0],
- "8717": [0, 0.43056, 0, 0],
- "8722": [-0.03598, 0.46402, 0, 0],
- "8724": [0.08198, 0.69224, 0, 0],
- "8726": [0.08167, 0.58167, 0, 0],
- "8733": [0, 0.69224, 0, 0],
- "8736": [0, 0.69224, 0, 0],
- "8737": [0, 0.69224, 0, 0],
- "8738": [0.03517, 0.52239, 0, 0],
- "8739": [0.08167, 0.58167, 0, 0],
- "8740": [0.25142, 0.74111, 0, 0],
- "8741": [0.08167, 0.58167, 0, 0],
- "8742": [0.25142, 0.74111, 0, 0],
- "8756": [0, 0.69224, 0, 0],
- "8757": [0, 0.69224, 0, 0],
- "8764": [-0.13313, 0.36687, 0, 0],
- "8765": [-0.13313, 0.37788, 0, 0],
- "8769": [-0.13313, 0.36687, 0, 0],
- "8770": [-0.03625, 0.46375, 0, 0],
- "8774": [0.30274, 0.79383, 0, 0],
- "8776": [-0.01688, 0.48312, 0, 0],
- "8778": [0.08167, 0.58167, 0, 0],
- "8782": [0.06062, 0.54986, 0, 0],
- "8783": [0.06062, 0.54986, 0, 0],
- "8785": [0.08198, 0.58198, 0, 0],
- "8786": [0.08198, 0.58198, 0, 0],
- "8787": [0.08198, 0.58198, 0, 0],
- "8790": [0, 0.69224, 0, 0],
- "8791": [0.22958, 0.72958, 0, 0],
- "8796": [0.08198, 0.91667, 0, 0],
- "8806": [0.25583, 0.75583, 0, 0],
- "8807": [0.25583, 0.75583, 0, 0],
- "8808": [0.25142, 0.75726, 0, 0],
- "8809": [0.25142, 0.75726, 0, 0],
- "8812": [0.25583, 0.75583, 0, 0],
- "8814": [0.20576, 0.70576, 0, 0],
- "8815": [0.20576, 0.70576, 0, 0],
- "8816": [0.30274, 0.79383, 0, 0],
- "8817": [0.30274, 0.79383, 0, 0],
- "8818": [0.22958, 0.72958, 0, 0],
- "8819": [0.22958, 0.72958, 0, 0],
- "8822": [0.1808, 0.675, 0, 0],
- "8823": [0.1808, 0.675, 0, 0],
- "8828": [0.13667, 0.63667, 0, 0],
- "8829": [0.13667, 0.63667, 0, 0],
- "8830": [0.22958, 0.72958, 0, 0],
- "8831": [0.22958, 0.72958, 0, 0],
- "8832": [0.20576, 0.70576, 0, 0],
- "8833": [0.20576, 0.70576, 0, 0],
- "8840": [0.30274, 0.79383, 0, 0],
- "8841": [0.30274, 0.79383, 0, 0],
- "8842": [0.13597, 0.63597, 0, 0],
- "8843": [0.13597, 0.63597, 0, 0],
- "8847": [0.03517, 0.54986, 0, 0],
- "8848": [0.03517, 0.54986, 0, 0],
- "8858": [0.08198, 0.58198, 0, 0],
- "8859": [0.08198, 0.58198, 0, 0],
- "8861": [0.08198, 0.58198, 0, 0],
- "8862": [0, 0.675, 0, 0],
- "8863": [0, 0.675, 0, 0],
- "8864": [0, 0.675, 0, 0],
- "8865": [0, 0.675, 0, 0],
- "8872": [0, 0.69224, 0, 0],
- "8873": [0, 0.69224, 0, 0],
- "8874": [0, 0.69224, 0, 0],
- "8876": [0, 0.68889, 0, 0],
- "8877": [0, 0.68889, 0, 0],
- "8878": [0, 0.68889, 0, 0],
- "8879": [0, 0.68889, 0, 0],
- "8882": [0.03517, 0.54986, 0, 0],
- "8883": [0.03517, 0.54986, 0, 0],
- "8884": [0.13667, 0.63667, 0, 0],
- "8885": [0.13667, 0.63667, 0, 0],
- "8888": [0, 0.54986, 0, 0],
- "8890": [0.19444, 0.43056, 0, 0],
- "8891": [0.19444, 0.69224, 0, 0],
- "8892": [0.19444, 0.69224, 0, 0],
- "8901": [0, 0.54986, 0, 0],
- "8903": [0.08167, 0.58167, 0, 0],
- "8905": [0.08167, 0.58167, 0, 0],
- "8906": [0.08167, 0.58167, 0, 0],
- "8907": [0, 0.69224, 0, 0],
- "8908": [0, 0.69224, 0, 0],
- "8909": [-0.03598, 0.46402, 0, 0],
- "8910": [0, 0.54986, 0, 0],
- "8911": [0, 0.54986, 0, 0],
- "8912": [0.03517, 0.54986, 0, 0],
- "8913": [0.03517, 0.54986, 0, 0],
- "8914": [0, 0.54986, 0, 0],
- "8915": [0, 0.54986, 0, 0],
- "8916": [0, 0.69224, 0, 0],
- "8918": [0.0391, 0.5391, 0, 0],
- "8919": [0.0391, 0.5391, 0, 0],
- "8920": [0.03517, 0.54986, 0, 0],
- "8921": [0.03517, 0.54986, 0, 0],
- "8922": [0.38569, 0.88569, 0, 0],
- "8923": [0.38569, 0.88569, 0, 0],
- "8926": [0.13667, 0.63667, 0, 0],
- "8927": [0.13667, 0.63667, 0, 0],
- "8928": [0.30274, 0.79383, 0, 0],
- "8929": [0.30274, 0.79383, 0, 0],
- "8934": [0.23222, 0.74111, 0, 0],
- "8935": [0.23222, 0.74111, 0, 0],
- "8936": [0.23222, 0.74111, 0, 0],
- "8937": [0.23222, 0.74111, 0, 0],
- "8938": [0.20576, 0.70576, 0, 0],
- "8939": [0.20576, 0.70576, 0, 0],
- "8940": [0.30274, 0.79383, 0, 0],
- "8941": [0.30274, 0.79383, 0, 0],
- "8994": [0.19444, 0.69224, 0, 0],
- "8995": [0.19444, 0.69224, 0, 0],
- "9416": [0.15559, 0.69224, 0, 0],
- "9484": [0, 0.69224, 0, 0],
- "9488": [0, 0.69224, 0, 0],
- "9492": [0, 0.37788, 0, 0],
- "9496": [0, 0.37788, 0, 0],
- "9585": [0.19444, 0.68889, 0, 0],
- "9586": [0.19444, 0.74111, 0, 0],
- "9632": [0, 0.675, 0, 0],
- "9633": [0, 0.675, 0, 0],
- "9650": [0, 0.54986, 0, 0],
- "9651": [0, 0.54986, 0, 0],
- "9654": [0.03517, 0.54986, 0, 0],
- "9660": [0, 0.54986, 0, 0],
- "9661": [0, 0.54986, 0, 0],
- "9664": [0.03517, 0.54986, 0, 0],
- "9674": [0.11111, 0.69224, 0, 0],
- "9733": [0.19444, 0.69224, 0, 0],
- "10003": [0, 0.69224, 0, 0],
- "10016": [0, 0.69224, 0, 0],
- "10731": [0.11111, 0.69224, 0, 0],
- "10846": [0.19444, 0.75583, 0, 0],
- "10877": [0.13667, 0.63667, 0, 0],
- "10878": [0.13667, 0.63667, 0, 0],
- "10885": [0.25583, 0.75583, 0, 0],
- "10886": [0.25583, 0.75583, 0, 0],
- "10887": [0.13597, 0.63597, 0, 0],
- "10888": [0.13597, 0.63597, 0, 0],
- "10889": [0.26167, 0.75726, 0, 0],
- "10890": [0.26167, 0.75726, 0, 0],
- "10891": [0.48256, 0.98256, 0, 0],
- "10892": [0.48256, 0.98256, 0, 0],
- "10901": [0.13667, 0.63667, 0, 0],
- "10902": [0.13667, 0.63667, 0, 0],
- "10933": [0.25142, 0.75726, 0, 0],
- "10934": [0.25142, 0.75726, 0, 0],
- "10935": [0.26167, 0.75726, 0, 0],
- "10936": [0.26167, 0.75726, 0, 0],
- "10937": [0.26167, 0.75726, 0, 0],
- "10938": [0.26167, 0.75726, 0, 0],
- "10949": [0.25583, 0.75583, 0, 0],
- "10950": [0.25583, 0.75583, 0, 0],
- "10955": [0.28481, 0.79383, 0, 0],
- "10956": [0.28481, 0.79383, 0, 0],
- "57350": [0.08167, 0.58167, 0, 0],
- "57351": [0.08167, 0.58167, 0, 0],
- "57352": [0.08167, 0.58167, 0, 0],
- "57353": [0, 0.43056, 0.04028, 0],
- "57356": [0.25142, 0.75726, 0, 0],
- "57357": [0.25142, 0.75726, 0, 0],
- "57358": [0.41951, 0.91951, 0, 0],
- "57359": [0.30274, 0.79383, 0, 0],
- "57360": [0.30274, 0.79383, 0, 0],
- "57361": [0.41951, 0.91951, 0, 0],
- "57366": [0.25142, 0.75726, 0, 0],
- "57367": [0.25142, 0.75726, 0, 0],
- "57368": [0.25142, 0.75726, 0, 0],
- "57369": [0.25142, 0.75726, 0, 0],
- "57370": [0.13597, 0.63597, 0, 0],
- "57371": [0.13597, 0.63597, 0, 0],
- },
- "Caligraphic-Regular": {
- "48": [0, 0.43056, 0, 0],
- "49": [0, 0.43056, 0, 0],
- "50": [0, 0.43056, 0, 0],
- "51": [0.19444, 0.43056, 0, 0],
- "52": [0.19444, 0.43056, 0, 0],
- "53": [0.19444, 0.43056, 0, 0],
- "54": [0, 0.64444, 0, 0],
- "55": [0.19444, 0.43056, 0, 0],
- "56": [0, 0.64444, 0, 0],
- "57": [0.19444, 0.43056, 0, 0],
- "65": [0, 0.68333, 0, 0.19445],
- "66": [0, 0.68333, 0.03041, 0.13889],
- "67": [0, 0.68333, 0.05834, 0.13889],
- "68": [0, 0.68333, 0.02778, 0.08334],
- "69": [0, 0.68333, 0.08944, 0.11111],
- "70": [0, 0.68333, 0.09931, 0.11111],
- "71": [0.09722, 0.68333, 0.0593, 0.11111],
- "72": [0, 0.68333, 0.00965, 0.11111],
- "73": [0, 0.68333, 0.07382, 0],
- "74": [0.09722, 0.68333, 0.18472, 0.16667],
- "75": [0, 0.68333, 0.01445, 0.05556],
- "76": [0, 0.68333, 0, 0.13889],
- "77": [0, 0.68333, 0, 0.13889],
- "78": [0, 0.68333, 0.14736, 0.08334],
- "79": [0, 0.68333, 0.02778, 0.11111],
- "80": [0, 0.68333, 0.08222, 0.08334],
- "81": [0.09722, 0.68333, 0, 0.11111],
- "82": [0, 0.68333, 0, 0.08334],
- "83": [0, 0.68333, 0.075, 0.13889],
- "84": [0, 0.68333, 0.25417, 0],
- "85": [0, 0.68333, 0.09931, 0.08334],
- "86": [0, 0.68333, 0.08222, 0],
- "87": [0, 0.68333, 0.08222, 0.08334],
- "88": [0, 0.68333, 0.14643, 0.13889],
- "89": [0.09722, 0.68333, 0.08222, 0.08334],
- "90": [0, 0.68333, 0.07944, 0.13889],
- },
- "Fraktur-Regular": {
- "33": [0, 0.69141, 0, 0],
- "34": [0, 0.69141, 0, 0],
- "38": [0, 0.69141, 0, 0],
- "39": [0, 0.69141, 0, 0],
- "40": [0.24982, 0.74947, 0, 0],
- "41": [0.24982, 0.74947, 0, 0],
- "42": [0, 0.62119, 0, 0],
- "43": [0.08319, 0.58283, 0, 0],
- "44": [0, 0.10803, 0, 0],
- "45": [0.08319, 0.58283, 0, 0],
- "46": [0, 0.10803, 0, 0],
- "47": [0.24982, 0.74947, 0, 0],
- "48": [0, 0.47534, 0, 0],
- "49": [0, 0.47534, 0, 0],
- "50": [0, 0.47534, 0, 0],
- "51": [0.18906, 0.47534, 0, 0],
- "52": [0.18906, 0.47534, 0, 0],
- "53": [0.18906, 0.47534, 0, 0],
- "54": [0, 0.69141, 0, 0],
- "55": [0.18906, 0.47534, 0, 0],
- "56": [0, 0.69141, 0, 0],
- "57": [0.18906, 0.47534, 0, 0],
- "58": [0, 0.47534, 0, 0],
- "59": [0.12604, 0.47534, 0, 0],
- "61": [-0.13099, 0.36866, 0, 0],
- "63": [0, 0.69141, 0, 0],
- "65": [0, 0.69141, 0, 0],
- "66": [0, 0.69141, 0, 0],
- "67": [0, 0.69141, 0, 0],
- "68": [0, 0.69141, 0, 0],
- "69": [0, 0.69141, 0, 0],
- "70": [0.12604, 0.69141, 0, 0],
- "71": [0, 0.69141, 0, 0],
- "72": [0.06302, 0.69141, 0, 0],
- "73": [0, 0.69141, 0, 0],
- "74": [0.12604, 0.69141, 0, 0],
- "75": [0, 0.69141, 0, 0],
- "76": [0, 0.69141, 0, 0],
- "77": [0, 0.69141, 0, 0],
- "78": [0, 0.69141, 0, 0],
- "79": [0, 0.69141, 0, 0],
- "80": [0.18906, 0.69141, 0, 0],
- "81": [0.03781, 0.69141, 0, 0],
- "82": [0, 0.69141, 0, 0],
- "83": [0, 0.69141, 0, 0],
- "84": [0, 0.69141, 0, 0],
- "85": [0, 0.69141, 0, 0],
- "86": [0, 0.69141, 0, 0],
- "87": [0, 0.69141, 0, 0],
- "88": [0, 0.69141, 0, 0],
- "89": [0.18906, 0.69141, 0, 0],
- "90": [0.12604, 0.69141, 0, 0],
- "91": [0.24982, 0.74947, 0, 0],
- "93": [0.24982, 0.74947, 0, 0],
- "94": [0, 0.69141, 0, 0],
- "97": [0, 0.47534, 0, 0],
- "98": [0, 0.69141, 0, 0],
- "99": [0, 0.47534, 0, 0],
- "100": [0, 0.62119, 0, 0],
- "101": [0, 0.47534, 0, 0],
- "102": [0.18906, 0.69141, 0, 0],
- "103": [0.18906, 0.47534, 0, 0],
- "104": [0.18906, 0.69141, 0, 0],
- "105": [0, 0.69141, 0, 0],
- "106": [0, 0.69141, 0, 0],
- "107": [0, 0.69141, 0, 0],
- "108": [0, 0.69141, 0, 0],
- "109": [0, 0.47534, 0, 0],
- "110": [0, 0.47534, 0, 0],
- "111": [0, 0.47534, 0, 0],
- "112": [0.18906, 0.52396, 0, 0],
- "113": [0.18906, 0.47534, 0, 0],
- "114": [0, 0.47534, 0, 0],
- "115": [0, 0.47534, 0, 0],
- "116": [0, 0.62119, 0, 0],
- "117": [0, 0.47534, 0, 0],
- "118": [0, 0.52396, 0, 0],
- "119": [0, 0.52396, 0, 0],
- "120": [0.18906, 0.47534, 0, 0],
- "121": [0.18906, 0.47534, 0, 0],
- "122": [0.18906, 0.47534, 0, 0],
- "8216": [0, 0.69141, 0, 0],
- "8217": [0, 0.69141, 0, 0],
- "58112": [0, 0.62119, 0, 0],
- "58113": [0, 0.62119, 0, 0],
- "58114": [0.18906, 0.69141, 0, 0],
- "58115": [0.18906, 0.69141, 0, 0],
- "58116": [0.18906, 0.47534, 0, 0],
- "58117": [0, 0.69141, 0, 0],
- "58118": [0, 0.62119, 0, 0],
- "58119": [0, 0.47534, 0, 0],
- },
- "Main-Bold": {
- "33": [0, 0.69444, 0, 0],
- "34": [0, 0.69444, 0, 0],
- "35": [0.19444, 0.69444, 0, 0],
- "36": [0.05556, 0.75, 0, 0],
- "37": [0.05556, 0.75, 0, 0],
- "38": [0, 0.69444, 0, 0],
- "39": [0, 0.69444, 0, 0],
- "40": [0.25, 0.75, 0, 0],
- "41": [0.25, 0.75, 0, 0],
- "42": [0, 0.75, 0, 0],
- "43": [0.13333, 0.63333, 0, 0],
- "44": [0.19444, 0.15556, 0, 0],
- "45": [0, 0.44444, 0, 0],
- "46": [0, 0.15556, 0, 0],
- "47": [0.25, 0.75, 0, 0],
- "48": [0, 0.64444, 0, 0],
- "49": [0, 0.64444, 0, 0],
- "50": [0, 0.64444, 0, 0],
- "51": [0, 0.64444, 0, 0],
- "52": [0, 0.64444, 0, 0],
- "53": [0, 0.64444, 0, 0],
- "54": [0, 0.64444, 0, 0],
- "55": [0, 0.64444, 0, 0],
- "56": [0, 0.64444, 0, 0],
- "57": [0, 0.64444, 0, 0],
- "58": [0, 0.44444, 0, 0],
- "59": [0.19444, 0.44444, 0, 0],
- "60": [0.08556, 0.58556, 0, 0],
- "61": [-0.10889, 0.39111, 0, 0],
- "62": [0.08556, 0.58556, 0, 0],
- "63": [0, 0.69444, 0, 0],
- "64": [0, 0.69444, 0, 0],
- "65": [0, 0.68611, 0, 0],
- "66": [0, 0.68611, 0, 0],
- "67": [0, 0.68611, 0, 0],
- "68": [0, 0.68611, 0, 0],
- "69": [0, 0.68611, 0, 0],
- "70": [0, 0.68611, 0, 0],
- "71": [0, 0.68611, 0, 0],
- "72": [0, 0.68611, 0, 0],
- "73": [0, 0.68611, 0, 0],
- "74": [0, 0.68611, 0, 0],
- "75": [0, 0.68611, 0, 0],
- "76": [0, 0.68611, 0, 0],
- "77": [0, 0.68611, 0, 0],
- "78": [0, 0.68611, 0, 0],
- "79": [0, 0.68611, 0, 0],
- "80": [0, 0.68611, 0, 0],
- "81": [0.19444, 0.68611, 0, 0],
- "82": [0, 0.68611, 0, 0],
- "83": [0, 0.68611, 0, 0],
- "84": [0, 0.68611, 0, 0],
- "85": [0, 0.68611, 0, 0],
- "86": [0, 0.68611, 0.01597, 0],
- "87": [0, 0.68611, 0.01597, 0],
- "88": [0, 0.68611, 0, 0],
- "89": [0, 0.68611, 0.02875, 0],
- "90": [0, 0.68611, 0, 0],
- "91": [0.25, 0.75, 0, 0],
- "92": [0.25, 0.75, 0, 0],
- "93": [0.25, 0.75, 0, 0],
- "94": [0, 0.69444, 0, 0],
- "95": [0.31, 0.13444, 0.03194, 0],
- "96": [0, 0.69444, 0, 0],
- "97": [0, 0.44444, 0, 0],
- "98": [0, 0.69444, 0, 0],
- "99": [0, 0.44444, 0, 0],
- "100": [0, 0.69444, 0, 0],
- "101": [0, 0.44444, 0, 0],
- "102": [0, 0.69444, 0.10903, 0],
- "103": [0.19444, 0.44444, 0.01597, 0],
- "104": [0, 0.69444, 0, 0],
- "105": [0, 0.69444, 0, 0],
- "106": [0.19444, 0.69444, 0, 0],
- "107": [0, 0.69444, 0, 0],
- "108": [0, 0.69444, 0, 0],
- "109": [0, 0.44444, 0, 0],
- "110": [0, 0.44444, 0, 0],
- "111": [0, 0.44444, 0, 0],
- "112": [0.19444, 0.44444, 0, 0],
- "113": [0.19444, 0.44444, 0, 0],
- "114": [0, 0.44444, 0, 0],
- "115": [0, 0.44444, 0, 0],
- "116": [0, 0.63492, 0, 0],
- "117": [0, 0.44444, 0, 0],
- "118": [0, 0.44444, 0.01597, 0],
- "119": [0, 0.44444, 0.01597, 0],
- "120": [0, 0.44444, 0, 0],
- "121": [0.19444, 0.44444, 0.01597, 0],
- "122": [0, 0.44444, 0, 0],
- "123": [0.25, 0.75, 0, 0],
- "124": [0.25, 0.75, 0, 0],
- "125": [0.25, 0.75, 0, 0],
- "126": [0.35, 0.34444, 0, 0],
- "168": [0, 0.69444, 0, 0],
- "172": [0, 0.44444, 0, 0],
- "175": [0, 0.59611, 0, 0],
- "176": [0, 0.69444, 0, 0],
- "177": [0.13333, 0.63333, 0, 0],
- "180": [0, 0.69444, 0, 0],
- "215": [0.13333, 0.63333, 0, 0],
- "247": [0.13333, 0.63333, 0, 0],
- "305": [0, 0.44444, 0, 0],
- "567": [0.19444, 0.44444, 0, 0],
- "710": [0, 0.69444, 0, 0],
- "711": [0, 0.63194, 0, 0],
- "713": [0, 0.59611, 0, 0],
- "714": [0, 0.69444, 0, 0],
- "715": [0, 0.69444, 0, 0],
- "728": [0, 0.69444, 0, 0],
- "729": [0, 0.69444, 0, 0],
- "730": [0, 0.69444, 0, 0],
- "732": [0, 0.69444, 0, 0],
- "768": [0, 0.69444, 0, 0],
- "769": [0, 0.69444, 0, 0],
- "770": [0, 0.69444, 0, 0],
- "771": [0, 0.69444, 0, 0],
- "772": [0, 0.59611, 0, 0],
- "774": [0, 0.69444, 0, 0],
- "775": [0, 0.69444, 0, 0],
- "776": [0, 0.69444, 0, 0],
- "778": [0, 0.69444, 0, 0],
- "779": [0, 0.69444, 0, 0],
- "780": [0, 0.63194, 0, 0],
- "824": [0.19444, 0.69444, 0, 0],
- "915": [0, 0.68611, 0, 0],
- "916": [0, 0.68611, 0, 0],
- "920": [0, 0.68611, 0, 0],
- "923": [0, 0.68611, 0, 0],
- "926": [0, 0.68611, 0, 0],
- "928": [0, 0.68611, 0, 0],
- "931": [0, 0.68611, 0, 0],
- "933": [0, 0.68611, 0, 0],
- "934": [0, 0.68611, 0, 0],
- "936": [0, 0.68611, 0, 0],
- "937": [0, 0.68611, 0, 0],
- "8211": [0, 0.44444, 0.03194, 0],
- "8212": [0, 0.44444, 0.03194, 0],
- "8216": [0, 0.69444, 0, 0],
- "8217": [0, 0.69444, 0, 0],
- "8220": [0, 0.69444, 0, 0],
- "8221": [0, 0.69444, 0, 0],
- "8224": [0.19444, 0.69444, 0, 0],
- "8225": [0.19444, 0.69444, 0, 0],
- "8242": [0, 0.55556, 0, 0],
- "8407": [0, 0.72444, 0.15486, 0],
- "8463": [0, 0.69444, 0, 0],
- "8465": [0, 0.69444, 0, 0],
- "8467": [0, 0.69444, 0, 0],
- "8472": [0.19444, 0.44444, 0, 0],
- "8476": [0, 0.69444, 0, 0],
- "8501": [0, 0.69444, 0, 0],
- "8592": [-0.10889, 0.39111, 0, 0],
- "8593": [0.19444, 0.69444, 0, 0],
- "8594": [-0.10889, 0.39111, 0, 0],
- "8595": [0.19444, 0.69444, 0, 0],
- "8596": [-0.10889, 0.39111, 0, 0],
- "8597": [0.25, 0.75, 0, 0],
- "8598": [0.19444, 0.69444, 0, 0],
- "8599": [0.19444, 0.69444, 0, 0],
- "8600": [0.19444, 0.69444, 0, 0],
- "8601": [0.19444, 0.69444, 0, 0],
- "8636": [-0.10889, 0.39111, 0, 0],
- "8637": [-0.10889, 0.39111, 0, 0],
- "8640": [-0.10889, 0.39111, 0, 0],
- "8641": [-0.10889, 0.39111, 0, 0],
- "8656": [-0.10889, 0.39111, 0, 0],
- "8657": [0.19444, 0.69444, 0, 0],
- "8658": [-0.10889, 0.39111, 0, 0],
- "8659": [0.19444, 0.69444, 0, 0],
- "8660": [-0.10889, 0.39111, 0, 0],
- "8661": [0.25, 0.75, 0, 0],
- "8704": [0, 0.69444, 0, 0],
- "8706": [0, 0.69444, 0.06389, 0],
- "8707": [0, 0.69444, 0, 0],
- "8709": [0.05556, 0.75, 0, 0],
- "8711": [0, 0.68611, 0, 0],
- "8712": [0.08556, 0.58556, 0, 0],
- "8715": [0.08556, 0.58556, 0, 0],
- "8722": [0.13333, 0.63333, 0, 0],
- "8723": [0.13333, 0.63333, 0, 0],
- "8725": [0.25, 0.75, 0, 0],
- "8726": [0.25, 0.75, 0, 0],
- "8727": [-0.02778, 0.47222, 0, 0],
- "8728": [-0.02639, 0.47361, 0, 0],
- "8729": [-0.02639, 0.47361, 0, 0],
- "8730": [0.18, 0.82, 0, 0],
- "8733": [0, 0.44444, 0, 0],
- "8734": [0, 0.44444, 0, 0],
- "8736": [0, 0.69224, 0, 0],
- "8739": [0.25, 0.75, 0, 0],
- "8741": [0.25, 0.75, 0, 0],
- "8743": [0, 0.55556, 0, 0],
- "8744": [0, 0.55556, 0, 0],
- "8745": [0, 0.55556, 0, 0],
- "8746": [0, 0.55556, 0, 0],
- "8747": [0.19444, 0.69444, 0.12778, 0],
- "8764": [-0.10889, 0.39111, 0, 0],
- "8768": [0.19444, 0.69444, 0, 0],
- "8771": [0.00222, 0.50222, 0, 0],
- "8776": [0.02444, 0.52444, 0, 0],
- "8781": [0.00222, 0.50222, 0, 0],
- "8801": [0.00222, 0.50222, 0, 0],
- "8804": [0.19667, 0.69667, 0, 0],
- "8805": [0.19667, 0.69667, 0, 0],
- "8810": [0.08556, 0.58556, 0, 0],
- "8811": [0.08556, 0.58556, 0, 0],
- "8826": [0.08556, 0.58556, 0, 0],
- "8827": [0.08556, 0.58556, 0, 0],
- "8834": [0.08556, 0.58556, 0, 0],
- "8835": [0.08556, 0.58556, 0, 0],
- "8838": [0.19667, 0.69667, 0, 0],
- "8839": [0.19667, 0.69667, 0, 0],
- "8846": [0, 0.55556, 0, 0],
- "8849": [0.19667, 0.69667, 0, 0],
- "8850": [0.19667, 0.69667, 0, 0],
- "8851": [0, 0.55556, 0, 0],
- "8852": [0, 0.55556, 0, 0],
- "8853": [0.13333, 0.63333, 0, 0],
- "8854": [0.13333, 0.63333, 0, 0],
- "8855": [0.13333, 0.63333, 0, 0],
- "8856": [0.13333, 0.63333, 0, 0],
- "8857": [0.13333, 0.63333, 0, 0],
- "8866": [0, 0.69444, 0, 0],
- "8867": [0, 0.69444, 0, 0],
- "8868": [0, 0.69444, 0, 0],
- "8869": [0, 0.69444, 0, 0],
- "8900": [-0.02639, 0.47361, 0, 0],
- "8901": [-0.02639, 0.47361, 0, 0],
- "8902": [-0.02778, 0.47222, 0, 0],
- "8968": [0.25, 0.75, 0, 0],
- "8969": [0.25, 0.75, 0, 0],
- "8970": [0.25, 0.75, 0, 0],
- "8971": [0.25, 0.75, 0, 0],
- "8994": [-0.13889, 0.36111, 0, 0],
- "8995": [-0.13889, 0.36111, 0, 0],
- "9651": [0.19444, 0.69444, 0, 0],
- "9657": [-0.02778, 0.47222, 0, 0],
- "9661": [0.19444, 0.69444, 0, 0],
- "9667": [-0.02778, 0.47222, 0, 0],
- "9711": [0.19444, 0.69444, 0, 0],
- "9824": [0.12963, 0.69444, 0, 0],
- "9825": [0.12963, 0.69444, 0, 0],
- "9826": [0.12963, 0.69444, 0, 0],
- "9827": [0.12963, 0.69444, 0, 0],
- "9837": [0, 0.75, 0, 0],
- "9838": [0.19444, 0.69444, 0, 0],
- "9839": [0.19444, 0.69444, 0, 0],
- "10216": [0.25, 0.75, 0, 0],
- "10217": [0.25, 0.75, 0, 0],
- "10815": [0, 0.68611, 0, 0],
- "10927": [0.19667, 0.69667, 0, 0],
- "10928": [0.19667, 0.69667, 0, 0],
- },
- "Main-Italic": {
- "33": [0, 0.69444, 0.12417, 0],
- "34": [0, 0.69444, 0.06961, 0],
- "35": [0.19444, 0.69444, 0.06616, 0],
- "37": [0.05556, 0.75, 0.13639, 0],
- "38": [0, 0.69444, 0.09694, 0],
- "39": [0, 0.69444, 0.12417, 0],
- "40": [0.25, 0.75, 0.16194, 0],
- "41": [0.25, 0.75, 0.03694, 0],
- "42": [0, 0.75, 0.14917, 0],
- "43": [0.05667, 0.56167, 0.03694, 0],
- "44": [0.19444, 0.10556, 0, 0],
- "45": [0, 0.43056, 0.02826, 0],
- "46": [0, 0.10556, 0, 0],
- "47": [0.25, 0.75, 0.16194, 0],
- "48": [0, 0.64444, 0.13556, 0],
- "49": [0, 0.64444, 0.13556, 0],
- "50": [0, 0.64444, 0.13556, 0],
- "51": [0, 0.64444, 0.13556, 0],
- "52": [0.19444, 0.64444, 0.13556, 0],
- "53": [0, 0.64444, 0.13556, 0],
- "54": [0, 0.64444, 0.13556, 0],
- "55": [0.19444, 0.64444, 0.13556, 0],
- "56": [0, 0.64444, 0.13556, 0],
- "57": [0, 0.64444, 0.13556, 0],
- "58": [0, 0.43056, 0.0582, 0],
- "59": [0.19444, 0.43056, 0.0582, 0],
- "61": [-0.13313, 0.36687, 0.06616, 0],
- "63": [0, 0.69444, 0.1225, 0],
- "64": [0, 0.69444, 0.09597, 0],
- "65": [0, 0.68333, 0, 0],
- "66": [0, 0.68333, 0.10257, 0],
- "67": [0, 0.68333, 0.14528, 0],
- "68": [0, 0.68333, 0.09403, 0],
- "69": [0, 0.68333, 0.12028, 0],
- "70": [0, 0.68333, 0.13305, 0],
- "71": [0, 0.68333, 0.08722, 0],
- "72": [0, 0.68333, 0.16389, 0],
- "73": [0, 0.68333, 0.15806, 0],
- "74": [0, 0.68333, 0.14028, 0],
- "75": [0, 0.68333, 0.14528, 0],
- "76": [0, 0.68333, 0, 0],
- "77": [0, 0.68333, 0.16389, 0],
- "78": [0, 0.68333, 0.16389, 0],
- "79": [0, 0.68333, 0.09403, 0],
- "80": [0, 0.68333, 0.10257, 0],
- "81": [0.19444, 0.68333, 0.09403, 0],
- "82": [0, 0.68333, 0.03868, 0],
- "83": [0, 0.68333, 0.11972, 0],
- "84": [0, 0.68333, 0.13305, 0],
- "85": [0, 0.68333, 0.16389, 0],
- "86": [0, 0.68333, 0.18361, 0],
- "87": [0, 0.68333, 0.18361, 0],
- "88": [0, 0.68333, 0.15806, 0],
- "89": [0, 0.68333, 0.19383, 0],
- "90": [0, 0.68333, 0.14528, 0],
- "91": [0.25, 0.75, 0.1875, 0],
- "93": [0.25, 0.75, 0.10528, 0],
- "94": [0, 0.69444, 0.06646, 0],
- "95": [0.31, 0.12056, 0.09208, 0],
- "97": [0, 0.43056, 0.07671, 0],
- "98": [0, 0.69444, 0.06312, 0],
- "99": [0, 0.43056, 0.05653, 0],
- "100": [0, 0.69444, 0.10333, 0],
- "101": [0, 0.43056, 0.07514, 0],
- "102": [0.19444, 0.69444, 0.21194, 0],
- "103": [0.19444, 0.43056, 0.08847, 0],
- "104": [0, 0.69444, 0.07671, 0],
- "105": [0, 0.65536, 0.1019, 0],
- "106": [0.19444, 0.65536, 0.14467, 0],
- "107": [0, 0.69444, 0.10764, 0],
- "108": [0, 0.69444, 0.10333, 0],
- "109": [0, 0.43056, 0.07671, 0],
- "110": [0, 0.43056, 0.07671, 0],
- "111": [0, 0.43056, 0.06312, 0],
- "112": [0.19444, 0.43056, 0.06312, 0],
- "113": [0.19444, 0.43056, 0.08847, 0],
- "114": [0, 0.43056, 0.10764, 0],
- "115": [0, 0.43056, 0.08208, 0],
- "116": [0, 0.61508, 0.09486, 0],
- "117": [0, 0.43056, 0.07671, 0],
- "118": [0, 0.43056, 0.10764, 0],
- "119": [0, 0.43056, 0.10764, 0],
- "120": [0, 0.43056, 0.12042, 0],
- "121": [0.19444, 0.43056, 0.08847, 0],
- "122": [0, 0.43056, 0.12292, 0],
- "126": [0.35, 0.31786, 0.11585, 0],
- "163": [0, 0.69444, 0, 0],
- "305": [0, 0.43056, 0, 0.02778],
- "567": [0.19444, 0.43056, 0, 0.08334],
- "768": [0, 0.69444, 0, 0],
- "769": [0, 0.69444, 0.09694, 0],
- "770": [0, 0.69444, 0.06646, 0],
- "771": [0, 0.66786, 0.11585, 0],
- "772": [0, 0.56167, 0.10333, 0],
- "774": [0, 0.69444, 0.10806, 0],
- "775": [0, 0.66786, 0.11752, 0],
- "776": [0, 0.66786, 0.10474, 0],
- "778": [0, 0.69444, 0, 0],
- "779": [0, 0.69444, 0.1225, 0],
- "780": [0, 0.62847, 0.08295, 0],
- "915": [0, 0.68333, 0.13305, 0],
- "916": [0, 0.68333, 0, 0],
- "920": [0, 0.68333, 0.09403, 0],
- "923": [0, 0.68333, 0, 0],
- "926": [0, 0.68333, 0.15294, 0],
- "928": [0, 0.68333, 0.16389, 0],
- "931": [0, 0.68333, 0.12028, 0],
- "933": [0, 0.68333, 0.11111, 0],
- "934": [0, 0.68333, 0.05986, 0],
- "936": [0, 0.68333, 0.11111, 0],
- "937": [0, 0.68333, 0.10257, 0],
- "8211": [0, 0.43056, 0.09208, 0],
- "8212": [0, 0.43056, 0.09208, 0],
- "8216": [0, 0.69444, 0.12417, 0],
- "8217": [0, 0.69444, 0.12417, 0],
- "8220": [0, 0.69444, 0.1685, 0],
- "8221": [0, 0.69444, 0.06961, 0],
- "8463": [0, 0.68889, 0, 0],
- },
- "Main-Regular": {
- "32": [0, 0, 0, 0],
- "33": [0, 0.69444, 0, 0],
- "34": [0, 0.69444, 0, 0],
- "35": [0.19444, 0.69444, 0, 0],
- "36": [0.05556, 0.75, 0, 0],
- "37": [0.05556, 0.75, 0, 0],
- "38": [0, 0.69444, 0, 0],
- "39": [0, 0.69444, 0, 0],
- "40": [0.25, 0.75, 0, 0],
- "41": [0.25, 0.75, 0, 0],
- "42": [0, 0.75, 0, 0],
- "43": [0.08333, 0.58333, 0, 0],
- "44": [0.19444, 0.10556, 0, 0],
- "45": [0, 0.43056, 0, 0],
- "46": [0, 0.10556, 0, 0],
- "47": [0.25, 0.75, 0, 0],
- "48": [0, 0.64444, 0, 0],
- "49": [0, 0.64444, 0, 0],
- "50": [0, 0.64444, 0, 0],
- "51": [0, 0.64444, 0, 0],
- "52": [0, 0.64444, 0, 0],
- "53": [0, 0.64444, 0, 0],
- "54": [0, 0.64444, 0, 0],
- "55": [0, 0.64444, 0, 0],
- "56": [0, 0.64444, 0, 0],
- "57": [0, 0.64444, 0, 0],
- "58": [0, 0.43056, 0, 0],
- "59": [0.19444, 0.43056, 0, 0],
- "60": [0.0391, 0.5391, 0, 0],
- "61": [-0.13313, 0.36687, 0, 0],
- "62": [0.0391, 0.5391, 0, 0],
- "63": [0, 0.69444, 0, 0],
- "64": [0, 0.69444, 0, 0],
- "65": [0, 0.68333, 0, 0],
- "66": [0, 0.68333, 0, 0],
- "67": [0, 0.68333, 0, 0],
- "68": [0, 0.68333, 0, 0],
- "69": [0, 0.68333, 0, 0],
- "70": [0, 0.68333, 0, 0],
- "71": [0, 0.68333, 0, 0],
- "72": [0, 0.68333, 0, 0],
- "73": [0, 0.68333, 0, 0],
- "74": [0, 0.68333, 0, 0],
- "75": [0, 0.68333, 0, 0],
- "76": [0, 0.68333, 0, 0],
- "77": [0, 0.68333, 0, 0],
- "78": [0, 0.68333, 0, 0],
- "79": [0, 0.68333, 0, 0],
- "80": [0, 0.68333, 0, 0],
- "81": [0.19444, 0.68333, 0, 0],
- "82": [0, 0.68333, 0, 0],
- "83": [0, 0.68333, 0, 0],
- "84": [0, 0.68333, 0, 0],
- "85": [0, 0.68333, 0, 0],
- "86": [0, 0.68333, 0.01389, 0],
- "87": [0, 0.68333, 0.01389, 0],
- "88": [0, 0.68333, 0, 0],
- "89": [0, 0.68333, 0.025, 0],
- "90": [0, 0.68333, 0, 0],
- "91": [0.25, 0.75, 0, 0],
- "92": [0.25, 0.75, 0, 0],
- "93": [0.25, 0.75, 0, 0],
- "94": [0, 0.69444, 0, 0],
- "95": [0.31, 0.12056, 0.02778, 0],
- "96": [0, 0.69444, 0, 0],
- "97": [0, 0.43056, 0, 0],
- "98": [0, 0.69444, 0, 0],
- "99": [0, 0.43056, 0, 0],
- "100": [0, 0.69444, 0, 0],
- "101": [0, 0.43056, 0, 0],
- "102": [0, 0.69444, 0.07778, 0],
- "103": [0.19444, 0.43056, 0.01389, 0],
- "104": [0, 0.69444, 0, 0],
- "105": [0, 0.66786, 0, 0],
- "106": [0.19444, 0.66786, 0, 0],
- "107": [0, 0.69444, 0, 0],
- "108": [0, 0.69444, 0, 0],
- "109": [0, 0.43056, 0, 0],
- "110": [0, 0.43056, 0, 0],
- "111": [0, 0.43056, 0, 0],
- "112": [0.19444, 0.43056, 0, 0],
- "113": [0.19444, 0.43056, 0, 0],
- "114": [0, 0.43056, 0, 0],
- "115": [0, 0.43056, 0, 0],
- "116": [0, 0.61508, 0, 0],
- "117": [0, 0.43056, 0, 0],
- "118": [0, 0.43056, 0.01389, 0],
- "119": [0, 0.43056, 0.01389, 0],
- "120": [0, 0.43056, 0, 0],
- "121": [0.19444, 0.43056, 0.01389, 0],
- "122": [0, 0.43056, 0, 0],
- "123": [0.25, 0.75, 0, 0],
- "124": [0.25, 0.75, 0, 0],
- "125": [0.25, 0.75, 0, 0],
- "126": [0.35, 0.31786, 0, 0],
- "160": [0, 0, 0, 0],
- "168": [0, 0.66786, 0, 0],
- "172": [0, 0.43056, 0, 0],
- "175": [0, 0.56778, 0, 0],
- "176": [0, 0.69444, 0, 0],
- "177": [0.08333, 0.58333, 0, 0],
- "180": [0, 0.69444, 0, 0],
- "215": [0.08333, 0.58333, 0, 0],
- "247": [0.08333, 0.58333, 0, 0],
- "305": [0, 0.43056, 0, 0],
- "567": [0.19444, 0.43056, 0, 0],
- "710": [0, 0.69444, 0, 0],
- "711": [0, 0.62847, 0, 0],
- "713": [0, 0.56778, 0, 0],
- "714": [0, 0.69444, 0, 0],
- "715": [0, 0.69444, 0, 0],
- "728": [0, 0.69444, 0, 0],
- "729": [0, 0.66786, 0, 0],
- "730": [0, 0.69444, 0, 0],
- "732": [0, 0.66786, 0, 0],
- "768": [0, 0.69444, 0, 0],
- "769": [0, 0.69444, 0, 0],
- "770": [0, 0.69444, 0, 0],
- "771": [0, 0.66786, 0, 0],
- "772": [0, 0.56778, 0, 0],
- "774": [0, 0.69444, 0, 0],
- "775": [0, 0.66786, 0, 0],
- "776": [0, 0.66786, 0, 0],
- "778": [0, 0.69444, 0, 0],
- "779": [0, 0.69444, 0, 0],
- "780": [0, 0.62847, 0, 0],
- "824": [0.19444, 0.69444, 0, 0],
- "915": [0, 0.68333, 0, 0],
- "916": [0, 0.68333, 0, 0],
- "920": [0, 0.68333, 0, 0],
- "923": [0, 0.68333, 0, 0],
- "926": [0, 0.68333, 0, 0],
- "928": [0, 0.68333, 0, 0],
- "931": [0, 0.68333, 0, 0],
- "933": [0, 0.68333, 0, 0],
- "934": [0, 0.68333, 0, 0],
- "936": [0, 0.68333, 0, 0],
- "937": [0, 0.68333, 0, 0],
- "8211": [0, 0.43056, 0.02778, 0],
- "8212": [0, 0.43056, 0.02778, 0],
- "8216": [0, 0.69444, 0, 0],
- "8217": [0, 0.69444, 0, 0],
- "8220": [0, 0.69444, 0, 0],
- "8221": [0, 0.69444, 0, 0],
- "8224": [0.19444, 0.69444, 0, 0],
- "8225": [0.19444, 0.69444, 0, 0],
- "8230": [0, 0.12, 0, 0],
- "8242": [0, 0.55556, 0, 0],
- "8407": [0, 0.71444, 0.15382, 0],
- "8463": [0, 0.68889, 0, 0],
- "8465": [0, 0.69444, 0, 0],
- "8467": [0, 0.69444, 0, 0.11111],
- "8472": [0.19444, 0.43056, 0, 0.11111],
- "8476": [0, 0.69444, 0, 0],
- "8501": [0, 0.69444, 0, 0],
- "8592": [-0.13313, 0.36687, 0, 0],
- "8593": [0.19444, 0.69444, 0, 0],
- "8594": [-0.13313, 0.36687, 0, 0],
- "8595": [0.19444, 0.69444, 0, 0],
- "8596": [-0.13313, 0.36687, 0, 0],
- "8597": [0.25, 0.75, 0, 0],
- "8598": [0.19444, 0.69444, 0, 0],
- "8599": [0.19444, 0.69444, 0, 0],
- "8600": [0.19444, 0.69444, 0, 0],
- "8601": [0.19444, 0.69444, 0, 0],
- "8614": [0.011, 0.511, 0, 0],
- "8617": [0.011, 0.511, 0, 0],
- "8618": [0.011, 0.511, 0, 0],
- "8636": [-0.13313, 0.36687, 0, 0],
- "8637": [-0.13313, 0.36687, 0, 0],
- "8640": [-0.13313, 0.36687, 0, 0],
- "8641": [-0.13313, 0.36687, 0, 0],
- "8652": [0.011, 0.671, 0, 0],
- "8656": [-0.13313, 0.36687, 0, 0],
- "8657": [0.19444, 0.69444, 0, 0],
- "8658": [-0.13313, 0.36687, 0, 0],
- "8659": [0.19444, 0.69444, 0, 0],
- "8660": [-0.13313, 0.36687, 0, 0],
- "8661": [0.25, 0.75, 0, 0],
- "8704": [0, 0.69444, 0, 0],
- "8706": [0, 0.69444, 0.05556, 0.08334],
- "8707": [0, 0.69444, 0, 0],
- "8709": [0.05556, 0.75, 0, 0],
- "8711": [0, 0.68333, 0, 0],
- "8712": [0.0391, 0.5391, 0, 0],
- "8715": [0.0391, 0.5391, 0, 0],
- "8722": [0.08333, 0.58333, 0, 0],
- "8723": [0.08333, 0.58333, 0, 0],
- "8725": [0.25, 0.75, 0, 0],
- "8726": [0.25, 0.75, 0, 0],
- "8727": [-0.03472, 0.46528, 0, 0],
- "8728": [-0.05555, 0.44445, 0, 0],
- "8729": [-0.05555, 0.44445, 0, 0],
- "8730": [0.2, 0.8, 0, 0],
- "8733": [0, 0.43056, 0, 0],
- "8734": [0, 0.43056, 0, 0],
- "8736": [0, 0.69224, 0, 0],
- "8739": [0.25, 0.75, 0, 0],
- "8741": [0.25, 0.75, 0, 0],
- "8743": [0, 0.55556, 0, 0],
- "8744": [0, 0.55556, 0, 0],
- "8745": [0, 0.55556, 0, 0],
- "8746": [0, 0.55556, 0, 0],
- "8747": [0.19444, 0.69444, 0.11111, 0],
- "8764": [-0.13313, 0.36687, 0, 0],
- "8768": [0.19444, 0.69444, 0, 0],
- "8771": [-0.03625, 0.46375, 0, 0],
- "8773": [-0.022, 0.589, 0, 0],
- "8776": [-0.01688, 0.48312, 0, 0],
- "8781": [-0.03625, 0.46375, 0, 0],
- "8784": [-0.133, 0.67, 0, 0],
- "8800": [0.215, 0.716, 0, 0],
- "8801": [-0.03625, 0.46375, 0, 0],
- "8804": [0.13597, 0.63597, 0, 0],
- "8805": [0.13597, 0.63597, 0, 0],
- "8810": [0.0391, 0.5391, 0, 0],
- "8811": [0.0391, 0.5391, 0, 0],
- "8826": [0.0391, 0.5391, 0, 0],
- "8827": [0.0391, 0.5391, 0, 0],
- "8834": [0.0391, 0.5391, 0, 0],
- "8835": [0.0391, 0.5391, 0, 0],
- "8838": [0.13597, 0.63597, 0, 0],
- "8839": [0.13597, 0.63597, 0, 0],
- "8846": [0, 0.55556, 0, 0],
- "8849": [0.13597, 0.63597, 0, 0],
- "8850": [0.13597, 0.63597, 0, 0],
- "8851": [0, 0.55556, 0, 0],
- "8852": [0, 0.55556, 0, 0],
- "8853": [0.08333, 0.58333, 0, 0],
- "8854": [0.08333, 0.58333, 0, 0],
- "8855": [0.08333, 0.58333, 0, 0],
- "8856": [0.08333, 0.58333, 0, 0],
- "8857": [0.08333, 0.58333, 0, 0],
- "8866": [0, 0.69444, 0, 0],
- "8867": [0, 0.69444, 0, 0],
- "8868": [0, 0.69444, 0, 0],
- "8869": [0, 0.69444, 0, 0],
- "8872": [0.249, 0.75, 0, 0],
- "8900": [-0.05555, 0.44445, 0, 0],
- "8901": [-0.05555, 0.44445, 0, 0],
- "8902": [-0.03472, 0.46528, 0, 0],
- "8904": [0.005, 0.505, 0, 0],
- "8942": [0.03, 0.9, 0, 0],
- "8943": [-0.19, 0.31, 0, 0],
- "8945": [-0.1, 0.82, 0, 0],
- "8968": [0.25, 0.75, 0, 0],
- "8969": [0.25, 0.75, 0, 0],
- "8970": [0.25, 0.75, 0, 0],
- "8971": [0.25, 0.75, 0, 0],
- "8994": [-0.14236, 0.35764, 0, 0],
- "8995": [-0.14236, 0.35764, 0, 0],
- "9136": [0.244, 0.744, 0, 0],
- "9137": [0.244, 0.744, 0, 0],
- "9651": [0.19444, 0.69444, 0, 0],
- "9657": [-0.03472, 0.46528, 0, 0],
- "9661": [0.19444, 0.69444, 0, 0],
- "9667": [-0.03472, 0.46528, 0, 0],
- "9711": [0.19444, 0.69444, 0, 0],
- "9824": [0.12963, 0.69444, 0, 0],
- "9825": [0.12963, 0.69444, 0, 0],
- "9826": [0.12963, 0.69444, 0, 0],
- "9827": [0.12963, 0.69444, 0, 0],
- "9837": [0, 0.75, 0, 0],
- "9838": [0.19444, 0.69444, 0, 0],
- "9839": [0.19444, 0.69444, 0, 0],
- "10216": [0.25, 0.75, 0, 0],
- "10217": [0.25, 0.75, 0, 0],
- "10222": [0.244, 0.744, 0, 0],
- "10223": [0.244, 0.744, 0, 0],
- "10229": [0.011, 0.511, 0, 0],
- "10230": [0.011, 0.511, 0, 0],
- "10231": [0.011, 0.511, 0, 0],
- "10232": [0.024, 0.525, 0, 0],
- "10233": [0.024, 0.525, 0, 0],
- "10234": [0.024, 0.525, 0, 0],
- "10236": [0.011, 0.511, 0, 0],
- "10815": [0, 0.68333, 0, 0],
- "10927": [0.13597, 0.63597, 0, 0],
- "10928": [0.13597, 0.63597, 0, 0],
- },
- "Math-BoldItalic": {
- "47": [0.19444, 0.69444, 0, 0],
- "65": [0, 0.68611, 0, 0],
- "66": [0, 0.68611, 0.04835, 0],
- "67": [0, 0.68611, 0.06979, 0],
- "68": [0, 0.68611, 0.03194, 0],
- "69": [0, 0.68611, 0.05451, 0],
- "70": [0, 0.68611, 0.15972, 0],
- "71": [0, 0.68611, 0, 0],
- "72": [0, 0.68611, 0.08229, 0],
- "73": [0, 0.68611, 0.07778, 0],
- "74": [0, 0.68611, 0.10069, 0],
- "75": [0, 0.68611, 0.06979, 0],
- "76": [0, 0.68611, 0, 0],
- "77": [0, 0.68611, 0.11424, 0],
- "78": [0, 0.68611, 0.11424, 0],
- "79": [0, 0.68611, 0.03194, 0],
- "80": [0, 0.68611, 0.15972, 0],
- "81": [0.19444, 0.68611, 0, 0],
- "82": [0, 0.68611, 0.00421, 0],
- "83": [0, 0.68611, 0.05382, 0],
- "84": [0, 0.68611, 0.15972, 0],
- "85": [0, 0.68611, 0.11424, 0],
- "86": [0, 0.68611, 0.25555, 0],
- "87": [0, 0.68611, 0.15972, 0],
- "88": [0, 0.68611, 0.07778, 0],
- "89": [0, 0.68611, 0.25555, 0],
- "90": [0, 0.68611, 0.06979, 0],
- "97": [0, 0.44444, 0, 0],
- "98": [0, 0.69444, 0, 0],
- "99": [0, 0.44444, 0, 0],
- "100": [0, 0.69444, 0, 0],
- "101": [0, 0.44444, 0, 0],
- "102": [0.19444, 0.69444, 0.11042, 0],
- "103": [0.19444, 0.44444, 0.03704, 0],
- "104": [0, 0.69444, 0, 0],
- "105": [0, 0.69326, 0, 0],
- "106": [0.19444, 0.69326, 0.0622, 0],
- "107": [0, 0.69444, 0.01852, 0],
- "108": [0, 0.69444, 0.0088, 0],
- "109": [0, 0.44444, 0, 0],
- "110": [0, 0.44444, 0, 0],
- "111": [0, 0.44444, 0, 0],
- "112": [0.19444, 0.44444, 0, 0],
- "113": [0.19444, 0.44444, 0.03704, 0],
- "114": [0, 0.44444, 0.03194, 0],
- "115": [0, 0.44444, 0, 0],
- "116": [0, 0.63492, 0, 0],
- "117": [0, 0.44444, 0, 0],
- "118": [0, 0.44444, 0.03704, 0],
- "119": [0, 0.44444, 0.02778, 0],
- "120": [0, 0.44444, 0, 0],
- "121": [0.19444, 0.44444, 0.03704, 0],
- "122": [0, 0.44444, 0.04213, 0],
- "915": [0, 0.68611, 0.15972, 0],
- "916": [0, 0.68611, 0, 0],
- "920": [0, 0.68611, 0.03194, 0],
- "923": [0, 0.68611, 0, 0],
- "926": [0, 0.68611, 0.07458, 0],
- "928": [0, 0.68611, 0.08229, 0],
- "931": [0, 0.68611, 0.05451, 0],
- "933": [0, 0.68611, 0.15972, 0],
- "934": [0, 0.68611, 0, 0],
- "936": [0, 0.68611, 0.11653, 0],
- "937": [0, 0.68611, 0.04835, 0],
- "945": [0, 0.44444, 0, 0],
- "946": [0.19444, 0.69444, 0.03403, 0],
- "947": [0.19444, 0.44444, 0.06389, 0],
- "948": [0, 0.69444, 0.03819, 0],
- "949": [0, 0.44444, 0, 0],
- "950": [0.19444, 0.69444, 0.06215, 0],
- "951": [0.19444, 0.44444, 0.03704, 0],
- "952": [0, 0.69444, 0.03194, 0],
- "953": [0, 0.44444, 0, 0],
- "954": [0, 0.44444, 0, 0],
- "955": [0, 0.69444, 0, 0],
- "956": [0.19444, 0.44444, 0, 0],
- "957": [0, 0.44444, 0.06898, 0],
- "958": [0.19444, 0.69444, 0.03021, 0],
- "959": [0, 0.44444, 0, 0],
- "960": [0, 0.44444, 0.03704, 0],
- "961": [0.19444, 0.44444, 0, 0],
- "962": [0.09722, 0.44444, 0.07917, 0],
- "963": [0, 0.44444, 0.03704, 0],
- "964": [0, 0.44444, 0.13472, 0],
- "965": [0, 0.44444, 0.03704, 0],
- "966": [0.19444, 0.44444, 0, 0],
- "967": [0.19444, 0.44444, 0, 0],
- "968": [0.19444, 0.69444, 0.03704, 0],
- "969": [0, 0.44444, 0.03704, 0],
- "977": [0, 0.69444, 0, 0],
- "981": [0.19444, 0.69444, 0, 0],
- "982": [0, 0.44444, 0.03194, 0],
- "1009": [0.19444, 0.44444, 0, 0],
- "1013": [0, 0.44444, 0, 0],
- },
- "Math-Italic": {
- "47": [0.19444, 0.69444, 0, 0],
- "65": [0, 0.68333, 0, 0.13889],
- "66": [0, 0.68333, 0.05017, 0.08334],
- "67": [0, 0.68333, 0.07153, 0.08334],
- "68": [0, 0.68333, 0.02778, 0.05556],
- "69": [0, 0.68333, 0.05764, 0.08334],
- "70": [0, 0.68333, 0.13889, 0.08334],
- "71": [0, 0.68333, 0, 0.08334],
- "72": [0, 0.68333, 0.08125, 0.05556],
- "73": [0, 0.68333, 0.07847, 0.11111],
- "74": [0, 0.68333, 0.09618, 0.16667],
- "75": [0, 0.68333, 0.07153, 0.05556],
- "76": [0, 0.68333, 0, 0.02778],
- "77": [0, 0.68333, 0.10903, 0.08334],
- "78": [0, 0.68333, 0.10903, 0.08334],
- "79": [0, 0.68333, 0.02778, 0.08334],
- "80": [0, 0.68333, 0.13889, 0.08334],
- "81": [0.19444, 0.68333, 0, 0.08334],
- "82": [0, 0.68333, 0.00773, 0.08334],
- "83": [0, 0.68333, 0.05764, 0.08334],
- "84": [0, 0.68333, 0.13889, 0.08334],
- "85": [0, 0.68333, 0.10903, 0.02778],
- "86": [0, 0.68333, 0.22222, 0],
- "87": [0, 0.68333, 0.13889, 0],
- "88": [0, 0.68333, 0.07847, 0.08334],
- "89": [0, 0.68333, 0.22222, 0],
- "90": [0, 0.68333, 0.07153, 0.08334],
- "97": [0, 0.43056, 0, 0],
- "98": [0, 0.69444, 0, 0],
- "99": [0, 0.43056, 0, 0.05556],
- "100": [0, 0.69444, 0, 0.16667],
- "101": [0, 0.43056, 0, 0.05556],
- "102": [0.19444, 0.69444, 0.10764, 0.16667],
- "103": [0.19444, 0.43056, 0.03588, 0.02778],
- "104": [0, 0.69444, 0, 0],
- "105": [0, 0.65952, 0, 0],
- "106": [0.19444, 0.65952, 0.05724, 0],
- "107": [0, 0.69444, 0.03148, 0],
- "108": [0, 0.69444, 0.01968, 0.08334],
- "109": [0, 0.43056, 0, 0],
- "110": [0, 0.43056, 0, 0],
- "111": [0, 0.43056, 0, 0.05556],
- "112": [0.19444, 0.43056, 0, 0.08334],
- "113": [0.19444, 0.43056, 0.03588, 0.08334],
- "114": [0, 0.43056, 0.02778, 0.05556],
- "115": [0, 0.43056, 0, 0.05556],
- "116": [0, 0.61508, 0, 0.08334],
- "117": [0, 0.43056, 0, 0.02778],
- "118": [0, 0.43056, 0.03588, 0.02778],
- "119": [0, 0.43056, 0.02691, 0.08334],
- "120": [0, 0.43056, 0, 0.02778],
- "121": [0.19444, 0.43056, 0.03588, 0.05556],
- "122": [0, 0.43056, 0.04398, 0.05556],
- "915": [0, 0.68333, 0.13889, 0.08334],
- "916": [0, 0.68333, 0, 0.16667],
- "920": [0, 0.68333, 0.02778, 0.08334],
- "923": [0, 0.68333, 0, 0.16667],
- "926": [0, 0.68333, 0.07569, 0.08334],
- "928": [0, 0.68333, 0.08125, 0.05556],
- "931": [0, 0.68333, 0.05764, 0.08334],
- "933": [0, 0.68333, 0.13889, 0.05556],
- "934": [0, 0.68333, 0, 0.08334],
- "936": [0, 0.68333, 0.11, 0.05556],
- "937": [0, 0.68333, 0.05017, 0.08334],
- "945": [0, 0.43056, 0.0037, 0.02778],
- "946": [0.19444, 0.69444, 0.05278, 0.08334],
- "947": [0.19444, 0.43056, 0.05556, 0],
- "948": [0, 0.69444, 0.03785, 0.05556],
- "949": [0, 0.43056, 0, 0.08334],
- "950": [0.19444, 0.69444, 0.07378, 0.08334],
- "951": [0.19444, 0.43056, 0.03588, 0.05556],
- "952": [0, 0.69444, 0.02778, 0.08334],
- "953": [0, 0.43056, 0, 0.05556],
- "954": [0, 0.43056, 0, 0],
- "955": [0, 0.69444, 0, 0],
- "956": [0.19444, 0.43056, 0, 0.02778],
- "957": [0, 0.43056, 0.06366, 0.02778],
- "958": [0.19444, 0.69444, 0.04601, 0.11111],
- "959": [0, 0.43056, 0, 0.05556],
- "960": [0, 0.43056, 0.03588, 0],
- "961": [0.19444, 0.43056, 0, 0.08334],
- "962": [0.09722, 0.43056, 0.07986, 0.08334],
- "963": [0, 0.43056, 0.03588, 0],
- "964": [0, 0.43056, 0.1132, 0.02778],
- "965": [0, 0.43056, 0.03588, 0.02778],
- "966": [0.19444, 0.43056, 0, 0.08334],
- "967": [0.19444, 0.43056, 0, 0.05556],
- "968": [0.19444, 0.69444, 0.03588, 0.11111],
- "969": [0, 0.43056, 0.03588, 0],
- "977": [0, 0.69444, 0, 0.08334],
- "981": [0.19444, 0.69444, 0, 0.08334],
- "982": [0, 0.43056, 0.02778, 0],
- "1009": [0.19444, 0.43056, 0, 0.08334],
- "1013": [0, 0.43056, 0, 0.05556],
- },
- "Math-Regular": {
- "65": [0, 0.68333, 0, 0.13889],
- "66": [0, 0.68333, 0.05017, 0.08334],
- "67": [0, 0.68333, 0.07153, 0.08334],
- "68": [0, 0.68333, 0.02778, 0.05556],
- "69": [0, 0.68333, 0.05764, 0.08334],
- "70": [0, 0.68333, 0.13889, 0.08334],
- "71": [0, 0.68333, 0, 0.08334],
- "72": [0, 0.68333, 0.08125, 0.05556],
- "73": [0, 0.68333, 0.07847, 0.11111],
- "74": [0, 0.68333, 0.09618, 0.16667],
- "75": [0, 0.68333, 0.07153, 0.05556],
- "76": [0, 0.68333, 0, 0.02778],
- "77": [0, 0.68333, 0.10903, 0.08334],
- "78": [0, 0.68333, 0.10903, 0.08334],
- "79": [0, 0.68333, 0.02778, 0.08334],
- "80": [0, 0.68333, 0.13889, 0.08334],
- "81": [0.19444, 0.68333, 0, 0.08334],
- "82": [0, 0.68333, 0.00773, 0.08334],
- "83": [0, 0.68333, 0.05764, 0.08334],
- "84": [0, 0.68333, 0.13889, 0.08334],
- "85": [0, 0.68333, 0.10903, 0.02778],
- "86": [0, 0.68333, 0.22222, 0],
- "87": [0, 0.68333, 0.13889, 0],
- "88": [0, 0.68333, 0.07847, 0.08334],
- "89": [0, 0.68333, 0.22222, 0],
- "90": [0, 0.68333, 0.07153, 0.08334],
- "97": [0, 0.43056, 0, 0],
- "98": [0, 0.69444, 0, 0],
- "99": [0, 0.43056, 0, 0.05556],
- "100": [0, 0.69444, 0, 0.16667],
- "101": [0, 0.43056, 0, 0.05556],
- "102": [0.19444, 0.69444, 0.10764, 0.16667],
- "103": [0.19444, 0.43056, 0.03588, 0.02778],
- "104": [0, 0.69444, 0, 0],
- "105": [0, 0.65952, 0, 0],
- "106": [0.19444, 0.65952, 0.05724, 0],
- "107": [0, 0.69444, 0.03148, 0],
- "108": [0, 0.69444, 0.01968, 0.08334],
- "109": [0, 0.43056, 0, 0],
- "110": [0, 0.43056, 0, 0],
- "111": [0, 0.43056, 0, 0.05556],
- "112": [0.19444, 0.43056, 0, 0.08334],
- "113": [0.19444, 0.43056, 0.03588, 0.08334],
- "114": [0, 0.43056, 0.02778, 0.05556],
- "115": [0, 0.43056, 0, 0.05556],
- "116": [0, 0.61508, 0, 0.08334],
- "117": [0, 0.43056, 0, 0.02778],
- "118": [0, 0.43056, 0.03588, 0.02778],
- "119": [0, 0.43056, 0.02691, 0.08334],
- "120": [0, 0.43056, 0, 0.02778],
- "121": [0.19444, 0.43056, 0.03588, 0.05556],
- "122": [0, 0.43056, 0.04398, 0.05556],
- "915": [0, 0.68333, 0.13889, 0.08334],
- "916": [0, 0.68333, 0, 0.16667],
- "920": [0, 0.68333, 0.02778, 0.08334],
- "923": [0, 0.68333, 0, 0.16667],
- "926": [0, 0.68333, 0.07569, 0.08334],
- "928": [0, 0.68333, 0.08125, 0.05556],
- "931": [0, 0.68333, 0.05764, 0.08334],
- "933": [0, 0.68333, 0.13889, 0.05556],
- "934": [0, 0.68333, 0, 0.08334],
- "936": [0, 0.68333, 0.11, 0.05556],
- "937": [0, 0.68333, 0.05017, 0.08334],
- "945": [0, 0.43056, 0.0037, 0.02778],
- "946": [0.19444, 0.69444, 0.05278, 0.08334],
- "947": [0.19444, 0.43056, 0.05556, 0],
- "948": [0, 0.69444, 0.03785, 0.05556],
- "949": [0, 0.43056, 0, 0.08334],
- "950": [0.19444, 0.69444, 0.07378, 0.08334],
- "951": [0.19444, 0.43056, 0.03588, 0.05556],
- "952": [0, 0.69444, 0.02778, 0.08334],
- "953": [0, 0.43056, 0, 0.05556],
- "954": [0, 0.43056, 0, 0],
- "955": [0, 0.69444, 0, 0],
- "956": [0.19444, 0.43056, 0, 0.02778],
- "957": [0, 0.43056, 0.06366, 0.02778],
- "958": [0.19444, 0.69444, 0.04601, 0.11111],
- "959": [0, 0.43056, 0, 0.05556],
- "960": [0, 0.43056, 0.03588, 0],
- "961": [0.19444, 0.43056, 0, 0.08334],
- "962": [0.09722, 0.43056, 0.07986, 0.08334],
- "963": [0, 0.43056, 0.03588, 0],
- "964": [0, 0.43056, 0.1132, 0.02778],
- "965": [0, 0.43056, 0.03588, 0.02778],
- "966": [0.19444, 0.43056, 0, 0.08334],
- "967": [0.19444, 0.43056, 0, 0.05556],
- "968": [0.19444, 0.69444, 0.03588, 0.11111],
- "969": [0, 0.43056, 0.03588, 0],
- "977": [0, 0.69444, 0, 0.08334],
- "981": [0.19444, 0.69444, 0, 0.08334],
- "982": [0, 0.43056, 0.02778, 0],
- "1009": [0.19444, 0.43056, 0, 0.08334],
- "1013": [0, 0.43056, 0, 0.05556],
- },
- "SansSerif-Regular": {
- "33": [0, 0.69444, 0, 0],
- "34": [0, 0.69444, 0, 0],
- "35": [0.19444, 0.69444, 0, 0],
- "36": [0.05556, 0.75, 0, 0],
- "37": [0.05556, 0.75, 0, 0],
- "38": [0, 0.69444, 0, 0],
- "39": [0, 0.69444, 0, 0],
- "40": [0.25, 0.75, 0, 0],
- "41": [0.25, 0.75, 0, 0],
- "42": [0, 0.75, 0, 0],
- "43": [0.08333, 0.58333, 0, 0],
- "44": [0.125, 0.08333, 0, 0],
- "45": [0, 0.44444, 0, 0],
- "46": [0, 0.08333, 0, 0],
- "47": [0.25, 0.75, 0, 0],
- "48": [0, 0.65556, 0, 0],
- "49": [0, 0.65556, 0, 0],
- "50": [0, 0.65556, 0, 0],
- "51": [0, 0.65556, 0, 0],
- "52": [0, 0.65556, 0, 0],
- "53": [0, 0.65556, 0, 0],
- "54": [0, 0.65556, 0, 0],
- "55": [0, 0.65556, 0, 0],
- "56": [0, 0.65556, 0, 0],
- "57": [0, 0.65556, 0, 0],
- "58": [0, 0.44444, 0, 0],
- "59": [0.125, 0.44444, 0, 0],
- "61": [-0.13, 0.37, 0, 0],
- "63": [0, 0.69444, 0, 0],
- "64": [0, 0.69444, 0, 0],
- "65": [0, 0.69444, 0, 0],
- "66": [0, 0.69444, 0, 0],
- "67": [0, 0.69444, 0, 0],
- "68": [0, 0.69444, 0, 0],
- "69": [0, 0.69444, 0, 0],
- "70": [0, 0.69444, 0, 0],
- "71": [0, 0.69444, 0, 0],
- "72": [0, 0.69444, 0, 0],
- "73": [0, 0.69444, 0, 0],
- "74": [0, 0.69444, 0, 0],
- "75": [0, 0.69444, 0, 0],
- "76": [0, 0.69444, 0, 0],
- "77": [0, 0.69444, 0, 0],
- "78": [0, 0.69444, 0, 0],
- "79": [0, 0.69444, 0, 0],
- "80": [0, 0.69444, 0, 0],
- "81": [0.125, 0.69444, 0, 0],
- "82": [0, 0.69444, 0, 0],
- "83": [0, 0.69444, 0, 0],
- "84": [0, 0.69444, 0, 0],
- "85": [0, 0.69444, 0, 0],
- "86": [0, 0.69444, 0.01389, 0],
- "87": [0, 0.69444, 0.01389, 0],
- "88": [0, 0.69444, 0, 0],
- "89": [0, 0.69444, 0.025, 0],
- "90": [0, 0.69444, 0, 0],
- "91": [0.25, 0.75, 0, 0],
- "93": [0.25, 0.75, 0, 0],
- "94": [0, 0.69444, 0, 0],
- "95": [0.35, 0.09444, 0.02778, 0],
- "97": [0, 0.44444, 0, 0],
- "98": [0, 0.69444, 0, 0],
- "99": [0, 0.44444, 0, 0],
- "100": [0, 0.69444, 0, 0],
- "101": [0, 0.44444, 0, 0],
- "102": [0, 0.69444, 0.06944, 0],
- "103": [0.19444, 0.44444, 0.01389, 0],
- "104": [0, 0.69444, 0, 0],
- "105": [0, 0.67937, 0, 0],
- "106": [0.19444, 0.67937, 0, 0],
- "107": [0, 0.69444, 0, 0],
- "108": [0, 0.69444, 0, 0],
- "109": [0, 0.44444, 0, 0],
- "110": [0, 0.44444, 0, 0],
- "111": [0, 0.44444, 0, 0],
- "112": [0.19444, 0.44444, 0, 0],
- "113": [0.19444, 0.44444, 0, 0],
- "114": [0, 0.44444, 0.01389, 0],
- "115": [0, 0.44444, 0, 0],
- "116": [0, 0.57143, 0, 0],
- "117": [0, 0.44444, 0, 0],
- "118": [0, 0.44444, 0.01389, 0],
- "119": [0, 0.44444, 0.01389, 0],
- "120": [0, 0.44444, 0, 0],
- "121": [0.19444, 0.44444, 0.01389, 0],
- "122": [0, 0.44444, 0, 0],
- "126": [0.35, 0.32659, 0, 0],
- "305": [0, 0.44444, 0, 0],
- "567": [0.19444, 0.44444, 0, 0],
- "768": [0, 0.69444, 0, 0],
- "769": [0, 0.69444, 0, 0],
- "770": [0, 0.69444, 0, 0],
- "771": [0, 0.67659, 0, 0],
- "772": [0, 0.60889, 0, 0],
- "774": [0, 0.69444, 0, 0],
- "775": [0, 0.67937, 0, 0],
- "776": [0, 0.67937, 0, 0],
- "778": [0, 0.69444, 0, 0],
- "779": [0, 0.69444, 0, 0],
- "780": [0, 0.63194, 0, 0],
- "915": [0, 0.69444, 0, 0],
- "916": [0, 0.69444, 0, 0],
- "920": [0, 0.69444, 0, 0],
- "923": [0, 0.69444, 0, 0],
- "926": [0, 0.69444, 0, 0],
- "928": [0, 0.69444, 0, 0],
- "931": [0, 0.69444, 0, 0],
- "933": [0, 0.69444, 0, 0],
- "934": [0, 0.69444, 0, 0],
- "936": [0, 0.69444, 0, 0],
- "937": [0, 0.69444, 0, 0],
- "8211": [0, 0.44444, 0.02778, 0],
- "8212": [0, 0.44444, 0.02778, 0],
- "8216": [0, 0.69444, 0, 0],
- "8217": [0, 0.69444, 0, 0],
- "8220": [0, 0.69444, 0, 0],
- "8221": [0, 0.69444, 0, 0],
- },
- "Script-Regular": {
- "65": [0, 0.7, 0.22925, 0],
- "66": [0, 0.7, 0.04087, 0],
- "67": [0, 0.7, 0.1689, 0],
- "68": [0, 0.7, 0.09371, 0],
- "69": [0, 0.7, 0.18583, 0],
- "70": [0, 0.7, 0.13634, 0],
- "71": [0, 0.7, 0.17322, 0],
- "72": [0, 0.7, 0.29694, 0],
- "73": [0, 0.7, 0.19189, 0],
- "74": [0.27778, 0.7, 0.19189, 0],
- "75": [0, 0.7, 0.31259, 0],
- "76": [0, 0.7, 0.19189, 0],
- "77": [0, 0.7, 0.15981, 0],
- "78": [0, 0.7, 0.3525, 0],
- "79": [0, 0.7, 0.08078, 0],
- "80": [0, 0.7, 0.08078, 0],
- "81": [0, 0.7, 0.03305, 0],
- "82": [0, 0.7, 0.06259, 0],
- "83": [0, 0.7, 0.19189, 0],
- "84": [0, 0.7, 0.29087, 0],
- "85": [0, 0.7, 0.25815, 0],
- "86": [0, 0.7, 0.27523, 0],
- "87": [0, 0.7, 0.27523, 0],
- "88": [0, 0.7, 0.26006, 0],
- "89": [0, 0.7, 0.2939, 0],
- "90": [0, 0.7, 0.24037, 0],
- },
- "Size1-Regular": {
- "40": [0.35001, 0.85, 0, 0],
- "41": [0.35001, 0.85, 0, 0],
- "47": [0.35001, 0.85, 0, 0],
- "91": [0.35001, 0.85, 0, 0],
- "92": [0.35001, 0.85, 0, 0],
- "93": [0.35001, 0.85, 0, 0],
- "123": [0.35001, 0.85, 0, 0],
- "125": [0.35001, 0.85, 0, 0],
- "710": [0, 0.72222, 0, 0],
- "732": [0, 0.72222, 0, 0],
- "770": [0, 0.72222, 0, 0],
- "771": [0, 0.72222, 0, 0],
- "8214": [-0.00099, 0.601, 0, 0],
- "8593": [1e-05, 0.6, 0, 0],
- "8595": [1e-05, 0.6, 0, 0],
- "8657": [1e-05, 0.6, 0, 0],
- "8659": [1e-05, 0.6, 0, 0],
- "8719": [0.25001, 0.75, 0, 0],
- "8720": [0.25001, 0.75, 0, 0],
- "8721": [0.25001, 0.75, 0, 0],
- "8730": [0.35001, 0.85, 0, 0],
- "8739": [-0.00599, 0.606, 0, 0],
- "8741": [-0.00599, 0.606, 0, 0],
- "8747": [0.30612, 0.805, 0.19445, 0],
- "8748": [0.306, 0.805, 0.19445, 0],
- "8749": [0.306, 0.805, 0.19445, 0],
- "8750": [0.30612, 0.805, 0.19445, 0],
- "8896": [0.25001, 0.75, 0, 0],
- "8897": [0.25001, 0.75, 0, 0],
- "8898": [0.25001, 0.75, 0, 0],
- "8899": [0.25001, 0.75, 0, 0],
- "8968": [0.35001, 0.85, 0, 0],
- "8969": [0.35001, 0.85, 0, 0],
- "8970": [0.35001, 0.85, 0, 0],
- "8971": [0.35001, 0.85, 0, 0],
- "9168": [-0.00099, 0.601, 0, 0],
- "10216": [0.35001, 0.85, 0, 0],
- "10217": [0.35001, 0.85, 0, 0],
- "10752": [0.25001, 0.75, 0, 0],
- "10753": [0.25001, 0.75, 0, 0],
- "10754": [0.25001, 0.75, 0, 0],
- "10756": [0.25001, 0.75, 0, 0],
- "10758": [0.25001, 0.75, 0, 0],
- },
- "Size2-Regular": {
- "40": [0.65002, 1.15, 0, 0],
- "41": [0.65002, 1.15, 0, 0],
- "47": [0.65002, 1.15, 0, 0],
- "91": [0.65002, 1.15, 0, 0],
- "92": [0.65002, 1.15, 0, 0],
- "93": [0.65002, 1.15, 0, 0],
- "123": [0.65002, 1.15, 0, 0],
- "125": [0.65002, 1.15, 0, 0],
- "710": [0, 0.75, 0, 0],
- "732": [0, 0.75, 0, 0],
- "770": [0, 0.75, 0, 0],
- "771": [0, 0.75, 0, 0],
- "8719": [0.55001, 1.05, 0, 0],
- "8720": [0.55001, 1.05, 0, 0],
- "8721": [0.55001, 1.05, 0, 0],
- "8730": [0.65002, 1.15, 0, 0],
- "8747": [0.86225, 1.36, 0.44445, 0],
- "8748": [0.862, 1.36, 0.44445, 0],
- "8749": [0.862, 1.36, 0.44445, 0],
- "8750": [0.86225, 1.36, 0.44445, 0],
- "8896": [0.55001, 1.05, 0, 0],
- "8897": [0.55001, 1.05, 0, 0],
- "8898": [0.55001, 1.05, 0, 0],
- "8899": [0.55001, 1.05, 0, 0],
- "8968": [0.65002, 1.15, 0, 0],
- "8969": [0.65002, 1.15, 0, 0],
- "8970": [0.65002, 1.15, 0, 0],
- "8971": [0.65002, 1.15, 0, 0],
- "10216": [0.65002, 1.15, 0, 0],
- "10217": [0.65002, 1.15, 0, 0],
- "10752": [0.55001, 1.05, 0, 0],
- "10753": [0.55001, 1.05, 0, 0],
- "10754": [0.55001, 1.05, 0, 0],
- "10756": [0.55001, 1.05, 0, 0],
- "10758": [0.55001, 1.05, 0, 0],
- },
- "Size3-Regular": {
- "40": [0.95003, 1.45, 0, 0],
- "41": [0.95003, 1.45, 0, 0],
- "47": [0.95003, 1.45, 0, 0],
- "91": [0.95003, 1.45, 0, 0],
- "92": [0.95003, 1.45, 0, 0],
- "93": [0.95003, 1.45, 0, 0],
- "123": [0.95003, 1.45, 0, 0],
- "125": [0.95003, 1.45, 0, 0],
- "710": [0, 0.75, 0, 0],
- "732": [0, 0.75, 0, 0],
- "770": [0, 0.75, 0, 0],
- "771": [0, 0.75, 0, 0],
- "8730": [0.95003, 1.45, 0, 0],
- "8968": [0.95003, 1.45, 0, 0],
- "8969": [0.95003, 1.45, 0, 0],
- "8970": [0.95003, 1.45, 0, 0],
- "8971": [0.95003, 1.45, 0, 0],
- "10216": [0.95003, 1.45, 0, 0],
- "10217": [0.95003, 1.45, 0, 0],
- },
- "Size4-Regular": {
- "40": [1.25003, 1.75, 0, 0],
- "41": [1.25003, 1.75, 0, 0],
- "47": [1.25003, 1.75, 0, 0],
- "91": [1.25003, 1.75, 0, 0],
- "92": [1.25003, 1.75, 0, 0],
- "93": [1.25003, 1.75, 0, 0],
- "123": [1.25003, 1.75, 0, 0],
- "125": [1.25003, 1.75, 0, 0],
- "710": [0, 0.825, 0, 0],
- "732": [0, 0.825, 0, 0],
- "770": [0, 0.825, 0, 0],
- "771": [0, 0.825, 0, 0],
- "8730": [1.25003, 1.75, 0, 0],
- "8968": [1.25003, 1.75, 0, 0],
- "8969": [1.25003, 1.75, 0, 0],
- "8970": [1.25003, 1.75, 0, 0],
- "8971": [1.25003, 1.75, 0, 0],
- "9115": [0.64502, 1.155, 0, 0],
- "9116": [1e-05, 0.6, 0, 0],
- "9117": [0.64502, 1.155, 0, 0],
- "9118": [0.64502, 1.155, 0, 0],
- "9119": [1e-05, 0.6, 0, 0],
- "9120": [0.64502, 1.155, 0, 0],
- "9121": [0.64502, 1.155, 0, 0],
- "9122": [-0.00099, 0.601, 0, 0],
- "9123": [0.64502, 1.155, 0, 0],
- "9124": [0.64502, 1.155, 0, 0],
- "9125": [-0.00099, 0.601, 0, 0],
- "9126": [0.64502, 1.155, 0, 0],
- "9127": [1e-05, 0.9, 0, 0],
- "9128": [0.65002, 1.15, 0, 0],
- "9129": [0.90001, 0, 0, 0],
- "9130": [0, 0.3, 0, 0],
- "9131": [1e-05, 0.9, 0, 0],
- "9132": [0.65002, 1.15, 0, 0],
- "9133": [0.90001, 0, 0, 0],
- "9143": [0.88502, 0.915, 0, 0],
- "10216": [1.25003, 1.75, 0, 0],
- "10217": [1.25003, 1.75, 0, 0],
- "57344": [-0.00499, 0.605, 0, 0],
- "57345": [-0.00499, 0.605, 0, 0],
- "57680": [0, 0.12, 0, 0],
- "57681": [0, 0.12, 0, 0],
- "57682": [0, 0.12, 0, 0],
- "57683": [0, 0.12, 0, 0],
- },
- "Typewriter-Regular": {
- "33": [0, 0.61111, 0, 0],
- "34": [0, 0.61111, 0, 0],
- "35": [0, 0.61111, 0, 0],
- "36": [0.08333, 0.69444, 0, 0],
- "37": [0.08333, 0.69444, 0, 0],
- "38": [0, 0.61111, 0, 0],
- "39": [0, 0.61111, 0, 0],
- "40": [0.08333, 0.69444, 0, 0],
- "41": [0.08333, 0.69444, 0, 0],
- "42": [0, 0.52083, 0, 0],
- "43": [-0.08056, 0.53055, 0, 0],
- "44": [0.13889, 0.125, 0, 0],
- "45": [-0.08056, 0.53055, 0, 0],
- "46": [0, 0.125, 0, 0],
- "47": [0.08333, 0.69444, 0, 0],
- "48": [0, 0.61111, 0, 0],
- "49": [0, 0.61111, 0, 0],
- "50": [0, 0.61111, 0, 0],
- "51": [0, 0.61111, 0, 0],
- "52": [0, 0.61111, 0, 0],
- "53": [0, 0.61111, 0, 0],
- "54": [0, 0.61111, 0, 0],
- "55": [0, 0.61111, 0, 0],
- "56": [0, 0.61111, 0, 0],
- "57": [0, 0.61111, 0, 0],
- "58": [0, 0.43056, 0, 0],
- "59": [0.13889, 0.43056, 0, 0],
- "60": [-0.05556, 0.55556, 0, 0],
- "61": [-0.19549, 0.41562, 0, 0],
- "62": [-0.05556, 0.55556, 0, 0],
- "63": [0, 0.61111, 0, 0],
- "64": [0, 0.61111, 0, 0],
- "65": [0, 0.61111, 0, 0],
- "66": [0, 0.61111, 0, 0],
- "67": [0, 0.61111, 0, 0],
- "68": [0, 0.61111, 0, 0],
- "69": [0, 0.61111, 0, 0],
- "70": [0, 0.61111, 0, 0],
- "71": [0, 0.61111, 0, 0],
- "72": [0, 0.61111, 0, 0],
- "73": [0, 0.61111, 0, 0],
- "74": [0, 0.61111, 0, 0],
- "75": [0, 0.61111, 0, 0],
- "76": [0, 0.61111, 0, 0],
- "77": [0, 0.61111, 0, 0],
- "78": [0, 0.61111, 0, 0],
- "79": [0, 0.61111, 0, 0],
- "80": [0, 0.61111, 0, 0],
- "81": [0.13889, 0.61111, 0, 0],
- "82": [0, 0.61111, 0, 0],
- "83": [0, 0.61111, 0, 0],
- "84": [0, 0.61111, 0, 0],
- "85": [0, 0.61111, 0, 0],
- "86": [0, 0.61111, 0, 0],
- "87": [0, 0.61111, 0, 0],
- "88": [0, 0.61111, 0, 0],
- "89": [0, 0.61111, 0, 0],
- "90": [0, 0.61111, 0, 0],
- "91": [0.08333, 0.69444, 0, 0],
- "92": [0.08333, 0.69444, 0, 0],
- "93": [0.08333, 0.69444, 0, 0],
- "94": [0, 0.61111, 0, 0],
- "95": [0.09514, 0, 0, 0],
- "96": [0, 0.61111, 0, 0],
- "97": [0, 0.43056, 0, 0],
- "98": [0, 0.61111, 0, 0],
- "99": [0, 0.43056, 0, 0],
- "100": [0, 0.61111, 0, 0],
- "101": [0, 0.43056, 0, 0],
- "102": [0, 0.61111, 0, 0],
- "103": [0.22222, 0.43056, 0, 0],
- "104": [0, 0.61111, 0, 0],
- "105": [0, 0.61111, 0, 0],
- "106": [0.22222, 0.61111, 0, 0],
- "107": [0, 0.61111, 0, 0],
- "108": [0, 0.61111, 0, 0],
- "109": [0, 0.43056, 0, 0],
- "110": [0, 0.43056, 0, 0],
- "111": [0, 0.43056, 0, 0],
- "112": [0.22222, 0.43056, 0, 0],
- "113": [0.22222, 0.43056, 0, 0],
- "114": [0, 0.43056, 0, 0],
- "115": [0, 0.43056, 0, 0],
- "116": [0, 0.55358, 0, 0],
- "117": [0, 0.43056, 0, 0],
- "118": [0, 0.43056, 0, 0],
- "119": [0, 0.43056, 0, 0],
- "120": [0, 0.43056, 0, 0],
- "121": [0.22222, 0.43056, 0, 0],
- "122": [0, 0.43056, 0, 0],
- "123": [0.08333, 0.69444, 0, 0],
- "124": [0.08333, 0.69444, 0, 0],
- "125": [0.08333, 0.69444, 0, 0],
- "126": [0, 0.61111, 0, 0],
- "127": [0, 0.61111, 0, 0],
- "305": [0, 0.43056, 0, 0],
- "567": [0.22222, 0.43056, 0, 0],
- "768": [0, 0.61111, 0, 0],
- "769": [0, 0.61111, 0, 0],
- "770": [0, 0.61111, 0, 0],
- "771": [0, 0.61111, 0, 0],
- "772": [0, 0.56555, 0, 0],
- "774": [0, 0.61111, 0, 0],
- "776": [0, 0.61111, 0, 0],
- "778": [0, 0.61111, 0, 0],
- "780": [0, 0.56597, 0, 0],
- "915": [0, 0.61111, 0, 0],
- "916": [0, 0.61111, 0, 0],
- "920": [0, 0.61111, 0, 0],
- "923": [0, 0.61111, 0, 0],
- "926": [0, 0.61111, 0, 0],
- "928": [0, 0.61111, 0, 0],
- "931": [0, 0.61111, 0, 0],
- "933": [0, 0.61111, 0, 0],
- "934": [0, 0.61111, 0, 0],
- "936": [0, 0.61111, 0, 0],
- "937": [0, 0.61111, 0, 0],
- "2018": [0, 0.61111, 0, 0],
- "2019": [0, 0.61111, 0, 0],
- "8242": [0, 0.61111, 0, 0],
- },
-};
-
-},{}],19:[function(require,module,exports){
-var utils = require("./utils");
-var ParseError = require("./ParseError");
-
-/* This file contains a list of functions that we parse, identified by
- * the calls to defineFunction.
- *
- * The first argument to defineFunction is a single name or a list of names.
- * All functions named in such a list will share a single implementation.
- *
- * Each declared function can have associated properties, which
- * include the following:
- *
- * - numArgs: The number of arguments the function takes.
- * If this is the only property, it can be passed as a number
- * instead of an element of a properties object.
- * - argTypes: (optional) An array corresponding to each argument of the
- * function, giving the type of argument that should be parsed. Its
- * length should be equal to `numArgs + numOptionalArgs`. Valid
- * types:
- * - "size": A size-like thing, such as "1em" or "5ex"
- * - "color": An html color, like "#abc" or "blue"
- * - "original": The same type as the environment that the
- * function being parsed is in (e.g. used for the
- * bodies of functions like \color where the first
- * argument is special and the second argument is
- * parsed normally)
- * Other possible types (probably shouldn't be used)
- * - "text": Text-like (e.g. \text)
- * - "math": Normal math
- * If undefined, this will be treated as an appropriate length
- * array of "original" strings
- * - greediness: (optional) The greediness of the function to use ungrouped
- * arguments.
- *
- * E.g. if you have an expression
- * \sqrt \frac 1 2
- * since \frac has greediness=2 vs \sqrt's greediness=1, \frac
- * will use the two arguments '1' and '2' as its two arguments,
- * then that whole function will be used as the argument to
- * \sqrt. On the other hand, the expressions
- * \frac \frac 1 2 3
- * and
- * \frac \sqrt 1 2
- * will fail because \frac and \frac have equal greediness
- * and \sqrt has a lower greediness than \frac respectively. To
- * make these parse, we would have to change them to:
- * \frac {\frac 1 2} 3
- * and
- * \frac {\sqrt 1} 2
- *
- * The default value is `1`
- * - allowedInText: (optional) Whether or not the function is allowed inside
- * text mode (default false)
- * - numOptionalArgs: (optional) The number of optional arguments the function
- * should parse. If the optional arguments aren't found,
- * `null` will be passed to the handler in their place.
- * (default 0)
- * - infix: (optional) Must be true if the function is an infix operator.
- *
- * The last argument is that implementation, the handler for the function(s).
- * It is called to handle these functions and their arguments.
- * It receives two arguments:
- * - context contains information and references provided by the parser
- * - args is an array of arguments obtained from TeX input
- * The context contains the following properties:
- * - funcName: the text (i.e. name) of the function, including \
- * - parser: the parser object
- * - lexer: the lexer object
- * - positions: the positions in the overall string of the function
- * and the arguments.
- * The latter three should only be used to produce error messages.
- *
- * The function should return an object with the following keys:
- * - type: The type of element that this is. This is then used in
- * buildHTML/buildMathML to determine which function
- * should be called to build this node into a DOM node
- * Any other data can be added to the object, which will be passed
- * in to the function in buildHTML/buildMathML as `group.value`.
- */
-
-function defineFunction(names, props, handler) {
- if (typeof names === "string") {
- names = [names];
- }
- if (typeof props === "number") {
- props = { numArgs: props };
- }
- // Set default values of functions
- var data = {
- numArgs: props.numArgs,
- argTypes: props.argTypes,
- greediness: (props.greediness === undefined) ? 1 : props.greediness,
- allowedInText: !!props.allowedInText,
- numOptionalArgs: props.numOptionalArgs || 0,
- infix: !!props.infix,
- handler: handler,
- };
- for (var i = 0; i < names.length; ++i) {
- module.exports[names[i]] = data;
- }
-}
-
-// A normal square root
-defineFunction("\\sqrt", {
- numArgs: 1,
- numOptionalArgs: 1,
-}, function(context, args) {
- var index = args[0];
- var body = args[1];
- return {
- type: "sqrt",
- body: body,
- index: index,
- };
-});
-
-// Some non-mathy text
-defineFunction("\\text", {
- numArgs: 1,
- argTypes: ["text"],
- greediness: 2,
-}, function(context, args) {
- var body = args[0];
- // Since the corresponding buildHTML/buildMathML function expects a
- // list of elements, we normalize for different kinds of arguments
- // TODO(emily): maybe this should be done somewhere else
- var inner;
- if (body.type === "ordgroup") {
- inner = body.value;
- } else {
- inner = [body];
- }
-
- return {
- type: "text",
- body: inner,
- };
-});
-
-// A two-argument custom color
-defineFunction("\\color", {
- numArgs: 2,
- allowedInText: true,
- greediness: 3,
- argTypes: ["color", "original"],
-}, function(context, args) {
- var color = args[0];
- var body = args[1];
- // Normalize the different kinds of bodies (see \text above)
- var inner;
- if (body.type === "ordgroup") {
- inner = body.value;
- } else {
- inner = [body];
- }
-
- return {
- type: "color",
- color: color.value,
- value: inner,
- };
-});
-
-// An overline
-defineFunction("\\overline", {
- numArgs: 1,
-}, function(context, args) {
- var body = args[0];
- return {
- type: "overline",
- body: body,
- };
-});
-
-// An underline
-defineFunction("\\underline", {
- numArgs: 1,
-}, function(context, args) {
- var body = args[0];
- return {
- type: "underline",
- body: body,
- };
-});
-
-// A box of the width and height
-defineFunction("\\rule", {
- numArgs: 2,
- numOptionalArgs: 1,
- argTypes: ["size", "size", "size"],
-}, function(context, args) {
- var shift = args[0];
- var width = args[1];
- var height = args[2];
- return {
- type: "rule",
- shift: shift && shift.value,
- width: width.value,
- height: height.value,
- };
-});
-
-defineFunction("\\kern", {
- numArgs: 1,
- argTypes: ["size"],
-}, function(context, args) {
- return {
- type: "kern",
- dimension: args[0].value,
- };
-});
-
-// A KaTeX logo
-defineFunction("\\KaTeX", {
- numArgs: 0,
-}, function(context) {
- return {
- type: "katex",
- };
-});
-
-defineFunction("\\phantom", {
- numArgs: 1,
-}, function(context, args) {
- var body = args[0];
- var inner;
- if (body.type === "ordgroup") {
- inner = body.value;
- } else {
- inner = [body];
- }
-
- return {
- type: "phantom",
- value: inner,
- };
-});
-
-// Extra data needed for the delimiter handler down below
-var delimiterSizes = {
- "\\bigl" : {type: "open", size: 1},
- "\\Bigl" : {type: "open", size: 2},
- "\\biggl": {type: "open", size: 3},
- "\\Biggl": {type: "open", size: 4},
- "\\bigr" : {type: "close", size: 1},
- "\\Bigr" : {type: "close", size: 2},
- "\\biggr": {type: "close", size: 3},
- "\\Biggr": {type: "close", size: 4},
- "\\bigm" : {type: "rel", size: 1},
- "\\Bigm" : {type: "rel", size: 2},
- "\\biggm": {type: "rel", size: 3},
- "\\Biggm": {type: "rel", size: 4},
- "\\big" : {type: "textord", size: 1},
- "\\Big" : {type: "textord", size: 2},
- "\\bigg" : {type: "textord", size: 3},
- "\\Bigg" : {type: "textord", size: 4},
-};
-
-var delimiters = [
- "(", ")", "[", "\\lbrack", "]", "\\rbrack",
- "\\{", "\\lbrace", "\\}", "\\rbrace",
- "\\lfloor", "\\rfloor", "\\lceil", "\\rceil",
- "<", ">", "\\langle", "\\rangle", "\\lt", "\\gt",
- "\\lvert", "\\rvert", "\\lVert", "\\rVert",
- "\\lgroup", "\\rgroup", "\\lmoustache", "\\rmoustache",
- "/", "\\backslash",
- "|", "\\vert", "\\|", "\\Vert",
- "\\uparrow", "\\Uparrow",
- "\\downarrow", "\\Downarrow",
- "\\updownarrow", "\\Updownarrow",
- ".",
-];
-
-var fontAliases = {
- "\\Bbb": "\\mathbb",
- "\\bold": "\\mathbf",
- "\\frak": "\\mathfrak",
-};
-
-// Single-argument color functions
-defineFunction([
- "\\blue", "\\orange", "\\pink", "\\red",
- "\\green", "\\gray", "\\purple",
- "\\blueA", "\\blueB", "\\blueC", "\\blueD", "\\blueE",
- "\\tealA", "\\tealB", "\\tealC", "\\tealD", "\\tealE",
- "\\greenA", "\\greenB", "\\greenC", "\\greenD", "\\greenE",
- "\\goldA", "\\goldB", "\\goldC", "\\goldD", "\\goldE",
- "\\redA", "\\redB", "\\redC", "\\redD", "\\redE",
- "\\maroonA", "\\maroonB", "\\maroonC", "\\maroonD", "\\maroonE",
- "\\purpleA", "\\purpleB", "\\purpleC", "\\purpleD", "\\purpleE",
- "\\mintA", "\\mintB", "\\mintC",
- "\\grayA", "\\grayB", "\\grayC", "\\grayD", "\\grayE",
- "\\grayF", "\\grayG", "\\grayH", "\\grayI",
- "\\kaBlue", "\\kaGreen",
-], {
- numArgs: 1,
- allowedInText: true,
- greediness: 3,
-}, function(context, args) {
- var body = args[0];
- var atoms;
- if (body.type === "ordgroup") {
- atoms = body.value;
- } else {
- atoms = [body];
- }
-
- return {
- type: "color",
- color: "katex-" + context.funcName.slice(1),
- value: atoms,
- };
-});
-
-// There are 2 flags for operators; whether they produce limits in
-// displaystyle, and whether they are symbols and should grow in
-// displaystyle. These four groups cover the four possible choices.
-
-// No limits, not symbols
-defineFunction([
- "\\arcsin", "\\arccos", "\\arctan", "\\arg", "\\cos", "\\cosh",
- "\\cot", "\\coth", "\\csc", "\\deg", "\\dim", "\\exp", "\\hom",
- "\\ker", "\\lg", "\\ln", "\\log", "\\sec", "\\sin", "\\sinh",
- "\\tan", "\\tanh",
-], {
- numArgs: 0,
-}, function(context) {
- return {
- type: "op",
- limits: false,
- symbol: false,
- body: context.funcName,
- };
-});
-
-// Limits, not symbols
-defineFunction([
- "\\det", "\\gcd", "\\inf", "\\lim", "\\liminf", "\\limsup", "\\max",
- "\\min", "\\Pr", "\\sup",
-], {
- numArgs: 0,
-}, function(context) {
- return {
- type: "op",
- limits: true,
- symbol: false,
- body: context.funcName,
- };
-});
-
-// No limits, symbols
-defineFunction([
- "\\int", "\\iint", "\\iiint", "\\oint",
-], {
- numArgs: 0,
-}, function(context) {
- return {
- type: "op",
- limits: false,
- symbol: true,
- body: context.funcName,
- };
-});
-
-// Limits, symbols
-defineFunction([
- "\\coprod", "\\bigvee", "\\bigwedge", "\\biguplus", "\\bigcap",
- "\\bigcup", "\\intop", "\\prod", "\\sum", "\\bigotimes",
- "\\bigoplus", "\\bigodot", "\\bigsqcup", "\\smallint",
-], {
- numArgs: 0,
-}, function(context) {
- return {
- type: "op",
- limits: true,
- symbol: true,
- body: context.funcName,
- };
-});
-
-// Fractions
-defineFunction([
- "\\dfrac", "\\frac", "\\tfrac",
- "\\dbinom", "\\binom", "\\tbinom",
-], {
- numArgs: 2,
- greediness: 2,
-}, function(context, args) {
- var numer = args[0];
- var denom = args[1];
- var hasBarLine;
- var leftDelim = null;
- var rightDelim = null;
- var size = "auto";
-
- switch (context.funcName) {
- case "\\dfrac":
- case "\\frac":
- case "\\tfrac":
- hasBarLine = true;
- break;
- case "\\dbinom":
- case "\\binom":
- case "\\tbinom":
- hasBarLine = false;
- leftDelim = "(";
- rightDelim = ")";
- break;
- default:
- throw new Error("Unrecognized genfrac command");
- }
-
- switch (context.funcName) {
- case "\\dfrac":
- case "\\dbinom":
- size = "display";
- break;
- case "\\tfrac":
- case "\\tbinom":
- size = "text";
- break;
- }
-
- return {
- type: "genfrac",
- numer: numer,
- denom: denom,
- hasBarLine: hasBarLine,
- leftDelim: leftDelim,
- rightDelim: rightDelim,
- size: size,
- };
-});
-
-// Left and right overlap functions
-defineFunction(["\\llap", "\\rlap"], {
- numArgs: 1,
- allowedInText: true,
-}, function(context, args) {
- var body = args[0];
- return {
- type: context.funcName.slice(1),
- body: body,
- };
-});
-
-// Delimiter functions
-defineFunction([
- "\\bigl", "\\Bigl", "\\biggl", "\\Biggl",
- "\\bigr", "\\Bigr", "\\biggr", "\\Biggr",
- "\\bigm", "\\Bigm", "\\biggm", "\\Biggm",
- "\\big", "\\Big", "\\bigg", "\\Bigg",
- "\\left", "\\right",
-], {
- numArgs: 1,
-}, function(context, args) {
- var delim = args[0];
- if (!utils.contains(delimiters, delim.value)) {
- throw new ParseError(
- "Invalid delimiter: '" + delim.value + "' after '" +
- context.funcName + "'", delim);
- }
-
- // \left and \right are caught somewhere in Parser.js, which is
- // why this data doesn't match what is in buildHTML.
- if (context.funcName === "\\left" || context.funcName === "\\right") {
- return {
- type: "leftright",
- value: delim.value,
- };
- } else {
- return {
- type: "delimsizing",
- size: delimiterSizes[context.funcName].size,
- delimType: delimiterSizes[context.funcName].type,
- value: delim.value,
- };
- }
-});
-
-// Sizing functions (handled in Parser.js explicitly, hence no handler)
-defineFunction([
- "\\tiny", "\\scriptsize", "\\footnotesize", "\\small",
- "\\normalsize", "\\large", "\\Large", "\\LARGE", "\\huge", "\\Huge",
-], 0, null);
-
-// Style changing functions (handled in Parser.js explicitly, hence no
-// handler)
-defineFunction([
- "\\displaystyle", "\\textstyle", "\\scriptstyle",
- "\\scriptscriptstyle",
-], 0, null);
-
-defineFunction([
- // styles
- "\\mathrm", "\\mathit", "\\mathbf",
-
- // families
- "\\mathbb", "\\mathcal", "\\mathfrak", "\\mathscr", "\\mathsf",
- "\\mathtt",
-
- // aliases
- "\\Bbb", "\\bold", "\\frak",
-], {
- numArgs: 1,
- greediness: 2,
-}, function(context, args) {
- var body = args[0];
- var func = context.funcName;
- if (func in fontAliases) {
- func = fontAliases[func];
- }
- return {
- type: "font",
- font: func.slice(1),
- body: body,
- };
-});
-
-// Accents
-defineFunction([
- "\\acute", "\\grave", "\\ddot", "\\tilde", "\\bar", "\\breve",
- "\\check", "\\hat", "\\vec", "\\dot",
- // We don't support expanding accents yet
- // "\\widetilde", "\\widehat"
-], {
- numArgs: 1,
-}, function(context, args) {
- var base = args[0];
- return {
- type: "accent",
- accent: context.funcName,
- base: base,
- };
-});
-
-// Infix generalized fractions
-defineFunction(["\\over", "\\choose"], {
- numArgs: 0,
- infix: true,
-}, function(context) {
- var replaceWith;
- switch (context.funcName) {
- case "\\over":
- replaceWith = "\\frac";
- break;
- case "\\choose":
- replaceWith = "\\binom";
- break;
- default:
- throw new Error("Unrecognized infix genfrac command");
- }
- return {
- type: "infix",
- replaceWith: replaceWith,
- token: context.token,
- };
-});
-
-// Row breaks for aligned data
-defineFunction(["\\\\", "\\cr"], {
- numArgs: 0,
- numOptionalArgs: 1,
- argTypes: ["size"],
-}, function(context, args) {
- var size = args[0];
- return {
- type: "cr",
- size: size,
- };
-});
-
-// Environment delimiters
-defineFunction(["\\begin", "\\end"], {
- numArgs: 1,
- argTypes: ["text"],
-}, function(context, args) {
- var nameGroup = args[0];
- if (nameGroup.type !== "ordgroup") {
- throw new ParseError("Invalid environment name", nameGroup);
- }
- var name = "";
- for (var i = 0; i < nameGroup.value.length; ++i) {
- name += nameGroup.value[i].value;
- }
- return {
- type: "environment",
- name: name,
- nameGroup: nameGroup,
- };
-});
-
-},{"./ParseError":6,"./utils":25}],20:[function(require,module,exports){
-/**
- * These objects store data about MathML nodes. This is the MathML equivalent
- * of the types in domTree.js. Since MathML handles its own rendering, and
- * since we're mainly using MathML to improve accessibility, we don't manage
- * any of the styling state that the plain DOM nodes do.
- *
- * The `toNode` and `toMarkup` functions work simlarly to how they do in
- * domTree.js, creating namespaced DOM nodes and HTML text markup respectively.
- */
-
-var utils = require("./utils");
-
-/**
- * This node represents a general purpose MathML node of any type. The
- * constructor requires the type of node to create (for example, `"mo"` or
- * `"mspace"`, corresponding to `<mo>` and `<mspace>` tags).
- */
-function MathNode(type, children) {
- this.type = type;
- this.attributes = {};
- this.children = children || [];
-}
-
-/**
- * Sets an attribute on a MathML node. MathML depends on attributes to convey a
- * semantic content, so this is used heavily.
- */
-MathNode.prototype.setAttribute = function(name, value) {
- this.attributes[name] = value;
-};
-
-/**
- * Converts the math node into a MathML-namespaced DOM element.
- */
-MathNode.prototype.toNode = function() {
- var node = document.createElementNS(
- "http://www.w3.org/1998/Math/MathML", this.type);
-
- for (var attr in this.attributes) {
- if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
- node.setAttribute(attr, this.attributes[attr]);
- }
- }
-
- for (var i = 0; i < this.children.length; i++) {
- node.appendChild(this.children[i].toNode());
- }
-
- return node;
-};
-
-/**
- * Converts the math node into an HTML markup string.
- */
-MathNode.prototype.toMarkup = function() {
- var markup = "<" + this.type;
-
- // Add the attributes
- for (var attr in this.attributes) {
- if (Object.prototype.hasOwnProperty.call(this.attributes, attr)) {
- markup += " " + attr + "=\"";
- markup += utils.escape(this.attributes[attr]);
- markup += "\"";
- }
- }
-
- markup += ">";
-
- for (var i = 0; i < this.children.length; i++) {
- markup += this.children[i].toMarkup();
- }
-
- markup += "</" + this.type + ">";
-
- return markup;
-};
-
-/**
- * This node represents a piece of text.
- */
-function TextNode(text) {
- this.text = text;
-}
-
-/**
- * Converts the text node into a DOM text node.
- */
-TextNode.prototype.toNode = function() {
- return document.createTextNode(this.text);
-};
-
-/**
- * Converts the text node into HTML markup (which is just the text itself).
- */
-TextNode.prototype.toMarkup = function() {
- return utils.escape(this.text);
-};
-
-module.exports = {
- MathNode: MathNode,
- TextNode: TextNode,
-};
-
-},{"./utils":25}],21:[function(require,module,exports){
-/**
- * The resulting parse tree nodes of the parse tree.
- *
- * It is possible to provide position information, so that a ParseNode can
- * fulfil a role similar to a Token in error reporting.
- * For details on the corresponding properties see Token constructor.
- * Providing such information can lead to better error reporting.
- *
- * @param {string} type type of node, like e.g. "ordgroup"
- * @param {?object} value type-specific representation of the node
- * @param {string} mode parse mode in action for this node,
- * "math" or "text"
- * @param {Token=} firstToken first token of the input for this node,
- * will omit position information if unset
- * @param {Token=} lastToken last token of the input for this node,
- * will default to firstToken if unset
- */
-function ParseNode(type, value, mode, firstToken, lastToken) {
- this.type = type;
- this.value = value;
- this.mode = mode;
- if (firstToken && (!lastToken || lastToken.lexer === firstToken.lexer)) {
- this.lexer = firstToken.lexer;
- this.start = firstToken.start;
- this.end = (lastToken || firstToken).end;
- }
-}
-
-module.exports = {
- ParseNode: ParseNode,
-};
-
-
-},{}],22:[function(require,module,exports){
-/**
- * Provides a single function for parsing an expression using a Parser
- * TODO(emily): Remove this
- */
-
-var Parser = require("./Parser");
-
-/**
- * Parses an expression using a Parser, then returns the parsed result.
- */
-var parseTree = function(toParse, settings) {
- if (!(typeof toParse === 'string' || toParse instanceof String)) {
- throw new TypeError('KaTeX can only parse string typed expression');
- }
- var parser = new Parser(toParse, settings);
-
- return parser.parse();
-};
-
-module.exports = parseTree;
-
-},{"./Parser":7}],23:[function(require,module,exports){
-/**
- * This file holds a list of all no-argument functions and single-character
- * symbols (like 'a' or ';').
- *
- * For each of the symbols, there are three properties they can have:
- * - font (required): the font to be used for this symbol. Either "main" (the
- normal font), or "ams" (the ams fonts).
- * - group (required): the ParseNode group type the symbol should have (i.e.
- "textord", "mathord", etc).
- See https://github.com/Khan/KaTeX/wiki/Examining-TeX#group-types
- * - replace: the character that this symbol or function should be
- * replaced with (i.e. "\phi" has a replace value of "\u03d5", the phi
- * character in the main font).
- *
- * The outermost map in the table indicates what mode the symbols should be
- * accepted in (e.g. "math" or "text").
- */
-
-module.exports = {
- math: {},
- text: {},
-};
-
-function defineSymbol(mode, font, group, replace, name) {
- module.exports[mode][name] = {
- font: font,
- group: group,
- replace: replace,
- };
-}
-
-// Some abbreviations for commonly used strings.
-// This helps minify the code, and also spotting typos using jshint.
-
-// modes:
-var math = "math";
-var text = "text";
-
-// fonts:
-var main = "main";
-var ams = "ams";
-
-// groups:
-var accent = "accent";
-var bin = "bin";
-var close = "close";
-var inner = "inner";
-var mathord = "mathord";
-var op = "op";
-var open = "open";
-var punct = "punct";
-var rel = "rel";
-var spacing = "spacing";
-var textord = "textord";
-
-// Now comes the symbol table
-
-// Relation Symbols
-defineSymbol(math, main, rel, "\u2261", "\\equiv");
-defineSymbol(math, main, rel, "\u227a", "\\prec");
-defineSymbol(math, main, rel, "\u227b", "\\succ");
-defineSymbol(math, main, rel, "\u223c", "\\sim");
-defineSymbol(math, main, rel, "\u22a5", "\\perp");
-defineSymbol(math, main, rel, "\u2aaf", "\\preceq");
-defineSymbol(math, main, rel, "\u2ab0", "\\succeq");
-defineSymbol(math, main, rel, "\u2243", "\\simeq");
-defineSymbol(math, main, rel, "\u2223", "\\mid");
-defineSymbol(math, main, rel, "\u226a", "\\ll");
-defineSymbol(math, main, rel, "\u226b", "\\gg");
-defineSymbol(math, main, rel, "\u224d", "\\asymp");
-defineSymbol(math, main, rel, "\u2225", "\\parallel");
-defineSymbol(math, main, rel, "\u22c8", "\\bowtie");
-defineSymbol(math, main, rel, "\u2323", "\\smile");
-defineSymbol(math, main, rel, "\u2291", "\\sqsubseteq");
-defineSymbol(math, main, rel, "\u2292", "\\sqsupseteq");
-defineSymbol(math, main, rel, "\u2250", "\\doteq");
-defineSymbol(math, main, rel, "\u2322", "\\frown");
-defineSymbol(math, main, rel, "\u220b", "\\ni");
-defineSymbol(math, main, rel, "\u221d", "\\propto");
-defineSymbol(math, main, rel, "\u22a2", "\\vdash");
-defineSymbol(math, main, rel, "\u22a3", "\\dashv");
-defineSymbol(math, main, rel, "\u220b", "\\owns");
-
-// Punctuation
-defineSymbol(math, main, punct, "\u002e", "\\ldotp");
-defineSymbol(math, main, punct, "\u22c5", "\\cdotp");
-
-// Misc Symbols
-defineSymbol(math, main, textord, "\u0023", "\\#");
-defineSymbol(math, main, textord, "\u0026", "\\&");
-defineSymbol(math, main, textord, "\u2135", "\\aleph");
-defineSymbol(math, main, textord, "\u2200", "\\forall");
-defineSymbol(math, main, textord, "\u210f", "\\hbar");
-defineSymbol(math, main, textord, "\u2203", "\\exists");
-defineSymbol(math, main, textord, "\u2207", "\\nabla");
-defineSymbol(math, main, textord, "\u266d", "\\flat");
-defineSymbol(math, main, textord, "\u2113", "\\ell");
-defineSymbol(math, main, textord, "\u266e", "\\natural");
-defineSymbol(math, main, textord, "\u2663", "\\clubsuit");
-defineSymbol(math, main, textord, "\u2118", "\\wp");
-defineSymbol(math, main, textord, "\u266f", "\\sharp");
-defineSymbol(math, main, textord, "\u2662", "\\diamondsuit");
-defineSymbol(math, main, textord, "\u211c", "\\Re");
-defineSymbol(math, main, textord, "\u2661", "\\heartsuit");
-defineSymbol(math, main, textord, "\u2111", "\\Im");
-defineSymbol(math, main, textord, "\u2660", "\\spadesuit");
-
-// Math and Text
-defineSymbol(math, main, textord, "\u2020", "\\dag");
-defineSymbol(math, main, textord, "\u2021", "\\ddag");
-
-// Large Delimiters
-defineSymbol(math, main, close, "\u23b1", "\\rmoustache");
-defineSymbol(math, main, open, "\u23b0", "\\lmoustache");
-defineSymbol(math, main, close, "\u27ef", "\\rgroup");
-defineSymbol(math, main, open, "\u27ee", "\\lgroup");
-
-// Binary Operators
-defineSymbol(math, main, bin, "\u2213", "\\mp");
-defineSymbol(math, main, bin, "\u2296", "\\ominus");
-defineSymbol(math, main, bin, "\u228e", "\\uplus");
-defineSymbol(math, main, bin, "\u2293", "\\sqcap");
-defineSymbol(math, main, bin, "\u2217", "\\ast");
-defineSymbol(math, main, bin, "\u2294", "\\sqcup");
-defineSymbol(math, main, bin, "\u25ef", "\\bigcirc");
-defineSymbol(math, main, bin, "\u2219", "\\bullet");
-defineSymbol(math, main, bin, "\u2021", "\\ddagger");
-defineSymbol(math, main, bin, "\u2240", "\\wr");
-defineSymbol(math, main, bin, "\u2a3f", "\\amalg");
-
-// Arrow Symbols
-defineSymbol(math, main, rel, "\u27f5", "\\longleftarrow");
-defineSymbol(math, main, rel, "\u21d0", "\\Leftarrow");
-defineSymbol(math, main, rel, "\u27f8", "\\Longleftarrow");
-defineSymbol(math, main, rel, "\u27f6", "\\longrightarrow");
-defineSymbol(math, main, rel, "\u21d2", "\\Rightarrow");
-defineSymbol(math, main, rel, "\u27f9", "\\Longrightarrow");
-defineSymbol(math, main, rel, "\u2194", "\\leftrightarrow");
-defineSymbol(math, main, rel, "\u27f7", "\\longleftrightarrow");
-defineSymbol(math, main, rel, "\u21d4", "\\Leftrightarrow");
-defineSymbol(math, main, rel, "\u27fa", "\\Longleftrightarrow");
-defineSymbol(math, main, rel, "\u21a6", "\\mapsto");
-defineSymbol(math, main, rel, "\u27fc", "\\longmapsto");
-defineSymbol(math, main, rel, "\u2197", "\\nearrow");
-defineSymbol(math, main, rel, "\u21a9", "\\hookleftarrow");
-defineSymbol(math, main, rel, "\u21aa", "\\hookrightarrow");
-defineSymbol(math, main, rel, "\u2198", "\\searrow");
-defineSymbol(math, main, rel, "\u21bc", "\\leftharpoonup");
-defineSymbol(math, main, rel, "\u21c0", "\\rightharpoonup");
-defineSymbol(math, main, rel, "\u2199", "\\swarrow");
-defineSymbol(math, main, rel, "\u21bd", "\\leftharpoondown");
-defineSymbol(math, main, rel, "\u21c1", "\\rightharpoondown");
-defineSymbol(math, main, rel, "\u2196", "\\nwarrow");
-defineSymbol(math, main, rel, "\u21cc", "\\rightleftharpoons");
-
-// AMS Negated Binary Relations
-defineSymbol(math, ams, rel, "\u226e", "\\nless");
-defineSymbol(math, ams, rel, "\ue010", "\\nleqslant");
-defineSymbol(math, ams, rel, "\ue011", "\\nleqq");
-defineSymbol(math, ams, rel, "\u2a87", "\\lneq");
-defineSymbol(math, ams, rel, "\u2268", "\\lneqq");
-defineSymbol(math, ams, rel, "\ue00c", "\\lvertneqq");
-defineSymbol(math, ams, rel, "\u22e6", "\\lnsim");
-defineSymbol(math, ams, rel, "\u2a89", "\\lnapprox");
-defineSymbol(math, ams, rel, "\u2280", "\\nprec");
-defineSymbol(math, ams, rel, "\u22e0", "\\npreceq");
-defineSymbol(math, ams, rel, "\u22e8", "\\precnsim");
-defineSymbol(math, ams, rel, "\u2ab9", "\\precnapprox");
-defineSymbol(math, ams, rel, "\u2241", "\\nsim");
-defineSymbol(math, ams, rel, "\ue006", "\\nshortmid");
-defineSymbol(math, ams, rel, "\u2224", "\\nmid");
-defineSymbol(math, ams, rel, "\u22ac", "\\nvdash");
-defineSymbol(math, ams, rel, "\u22ad", "\\nvDash");
-defineSymbol(math, ams, rel, "\u22ea", "\\ntriangleleft");
-defineSymbol(math, ams, rel, "\u22ec", "\\ntrianglelefteq");
-defineSymbol(math, ams, rel, "\u228a", "\\subsetneq");
-defineSymbol(math, ams, rel, "\ue01a", "\\varsubsetneq");
-defineSymbol(math, ams, rel, "\u2acb", "\\subsetneqq");
-defineSymbol(math, ams, rel, "\ue017", "\\varsubsetneqq");
-defineSymbol(math, ams, rel, "\u226f", "\\ngtr");
-defineSymbol(math, ams, rel, "\ue00f", "\\ngeqslant");
-defineSymbol(math, ams, rel, "\ue00e", "\\ngeqq");
-defineSymbol(math, ams, rel, "\u2a88", "\\gneq");
-defineSymbol(math, ams, rel, "\u2269", "\\gneqq");
-defineSymbol(math, ams, rel, "\ue00d", "\\gvertneqq");
-defineSymbol(math, ams, rel, "\u22e7", "\\gnsim");
-defineSymbol(math, ams, rel, "\u2a8a", "\\gnapprox");
-defineSymbol(math, ams, rel, "\u2281", "\\nsucc");
-defineSymbol(math, ams, rel, "\u22e1", "\\nsucceq");
-defineSymbol(math, ams, rel, "\u22e9", "\\succnsim");
-defineSymbol(math, ams, rel, "\u2aba", "\\succnapprox");
-defineSymbol(math, ams, rel, "\u2246", "\\ncong");
-defineSymbol(math, ams, rel, "\ue007", "\\nshortparallel");
-defineSymbol(math, ams, rel, "\u2226", "\\nparallel");
-defineSymbol(math, ams, rel, "\u22af", "\\nVDash");
-defineSymbol(math, ams, rel, "\u22eb", "\\ntriangleright");
-defineSymbol(math, ams, rel, "\u22ed", "\\ntrianglerighteq");
-defineSymbol(math, ams, rel, "\ue018", "\\nsupseteqq");
-defineSymbol(math, ams, rel, "\u228b", "\\supsetneq");
-defineSymbol(math, ams, rel, "\ue01b", "\\varsupsetneq");
-defineSymbol(math, ams, rel, "\u2acc", "\\supsetneqq");
-defineSymbol(math, ams, rel, "\ue019", "\\varsupsetneqq");
-defineSymbol(math, ams, rel, "\u22ae", "\\nVdash");
-defineSymbol(math, ams, rel, "\u2ab5", "\\precneqq");
-defineSymbol(math, ams, rel, "\u2ab6", "\\succneqq");
-defineSymbol(math, ams, rel, "\ue016", "\\nsubseteqq");
-defineSymbol(math, ams, bin, "\u22b4", "\\unlhd");
-defineSymbol(math, ams, bin, "\u22b5", "\\unrhd");
-
-// AMS Negated Arrows
-defineSymbol(math, ams, rel, "\u219a", "\\nleftarrow");
-defineSymbol(math, ams, rel, "\u219b", "\\nrightarrow");
-defineSymbol(math, ams, rel, "\u21cd", "\\nLeftarrow");
-defineSymbol(math, ams, rel, "\u21cf", "\\nRightarrow");
-defineSymbol(math, ams, rel, "\u21ae", "\\nleftrightarrow");
-defineSymbol(math, ams, rel, "\u21ce", "\\nLeftrightarrow");
-
-// AMS Misc
-defineSymbol(math, ams, rel, "\u25b3", "\\vartriangle");
-defineSymbol(math, ams, textord, "\u210f", "\\hslash");
-defineSymbol(math, ams, textord, "\u25bd", "\\triangledown");
-defineSymbol(math, ams, textord, "\u25ca", "\\lozenge");
-defineSymbol(math, ams, textord, "\u24c8", "\\circledS");
-defineSymbol(math, ams, textord, "\u00ae", "\\circledR");
-defineSymbol(math, ams, textord, "\u2221", "\\measuredangle");
-defineSymbol(math, ams, textord, "\u2204", "\\nexists");
-defineSymbol(math, ams, textord, "\u2127", "\\mho");
-defineSymbol(math, ams, textord, "\u2132", "\\Finv");
-defineSymbol(math, ams, textord, "\u2141", "\\Game");
-defineSymbol(math, ams, textord, "\u006b", "\\Bbbk");
-defineSymbol(math, ams, textord, "\u2035", "\\backprime");
-defineSymbol(math, ams, textord, "\u25b2", "\\blacktriangle");
-defineSymbol(math, ams, textord, "\u25bc", "\\blacktriangledown");
-defineSymbol(math, ams, textord, "\u25a0", "\\blacksquare");
-defineSymbol(math, ams, textord, "\u29eb", "\\blacklozenge");
-defineSymbol(math, ams, textord, "\u2605", "\\bigstar");
-defineSymbol(math, ams, textord, "\u2222", "\\sphericalangle");
-defineSymbol(math, ams, textord, "\u2201", "\\complement");
-defineSymbol(math, ams, textord, "\u00f0", "\\eth");
-defineSymbol(math, ams, textord, "\u2571", "\\diagup");
-defineSymbol(math, ams, textord, "\u2572", "\\diagdown");
-defineSymbol(math, ams, textord, "\u25a1", "\\square");
-defineSymbol(math, ams, textord, "\u25a1", "\\Box");
-defineSymbol(math, ams, textord, "\u25ca", "\\Diamond");
-defineSymbol(math, ams, textord, "\u00a5", "\\yen");
-defineSymbol(math, ams, textord, "\u2713", "\\checkmark");
-
-// AMS Hebrew
-defineSymbol(math, ams, textord, "\u2136", "\\beth");
-defineSymbol(math, ams, textord, "\u2138", "\\daleth");
-defineSymbol(math, ams, textord, "\u2137", "\\gimel");
-
-// AMS Greek
-defineSymbol(math, ams, textord, "\u03dd", "\\digamma");
-defineSymbol(math, ams, textord, "\u03f0", "\\varkappa");
-
-// AMS Delimiters
-defineSymbol(math, ams, open, "\u250c", "\\ulcorner");
-defineSymbol(math, ams, close, "\u2510", "\\urcorner");
-defineSymbol(math, ams, open, "\u2514", "\\llcorner");
-defineSymbol(math, ams, close, "\u2518", "\\lrcorner");
-
-// AMS Binary Relations
-defineSymbol(math, ams, rel, "\u2266", "\\leqq");
-defineSymbol(math, ams, rel, "\u2a7d", "\\leqslant");
-defineSymbol(math, ams, rel, "\u2a95", "\\eqslantless");
-defineSymbol(math, ams, rel, "\u2272", "\\lesssim");
-defineSymbol(math, ams, rel, "\u2a85", "\\lessapprox");
-defineSymbol(math, ams, rel, "\u224a", "\\approxeq");
-defineSymbol(math, ams, bin, "\u22d6", "\\lessdot");
-defineSymbol(math, ams, rel, "\u22d8", "\\lll");
-defineSymbol(math, ams, rel, "\u2276", "\\lessgtr");
-defineSymbol(math, ams, rel, "\u22da", "\\lesseqgtr");
-defineSymbol(math, ams, rel, "\u2a8b", "\\lesseqqgtr");
-defineSymbol(math, ams, rel, "\u2251", "\\doteqdot");
-defineSymbol(math, ams, rel, "\u2253", "\\risingdotseq");
-defineSymbol(math, ams, rel, "\u2252", "\\fallingdotseq");
-defineSymbol(math, ams, rel, "\u223d", "\\backsim");
-defineSymbol(math, ams, rel, "\u22cd", "\\backsimeq");
-defineSymbol(math, ams, rel, "\u2ac5", "\\subseteqq");
-defineSymbol(math, ams, rel, "\u22d0", "\\Subset");
-defineSymbol(math, ams, rel, "\u228f", "\\sqsubset");
-defineSymbol(math, ams, rel, "\u227c", "\\preccurlyeq");
-defineSymbol(math, ams, rel, "\u22de", "\\curlyeqprec");
-defineSymbol(math, ams, rel, "\u227e", "\\precsim");
-defineSymbol(math, ams, rel, "\u2ab7", "\\precapprox");
-defineSymbol(math, ams, rel, "\u22b2", "\\vartriangleleft");
-defineSymbol(math, ams, rel, "\u22b4", "\\trianglelefteq");
-defineSymbol(math, ams, rel, "\u22a8", "\\vDash");
-defineSymbol(math, ams, rel, "\u22aa", "\\Vvdash");
-defineSymbol(math, ams, rel, "\u2323", "\\smallsmile");
-defineSymbol(math, ams, rel, "\u2322", "\\smallfrown");
-defineSymbol(math, ams, rel, "\u224f", "\\bumpeq");
-defineSymbol(math, ams, rel, "\u224e", "\\Bumpeq");
-defineSymbol(math, ams, rel, "\u2267", "\\geqq");
-defineSymbol(math, ams, rel, "\u2a7e", "\\geqslant");
-defineSymbol(math, ams, rel, "\u2a96", "\\eqslantgtr");
-defineSymbol(math, ams, rel, "\u2273", "\\gtrsim");
-defineSymbol(math, ams, rel, "\u2a86", "\\gtrapprox");
-defineSymbol(math, ams, bin, "\u22d7", "\\gtrdot");
-defineSymbol(math, ams, rel, "\u22d9", "\\ggg");
-defineSymbol(math, ams, rel, "\u2277", "\\gtrless");
-defineSymbol(math, ams, rel, "\u22db", "\\gtreqless");
-defineSymbol(math, ams, rel, "\u2a8c", "\\gtreqqless");
-defineSymbol(math, ams, rel, "\u2256", "\\eqcirc");
-defineSymbol(math, ams, rel, "\u2257", "\\circeq");
-defineSymbol(math, ams, rel, "\u225c", "\\triangleq");
-defineSymbol(math, ams, rel, "\u223c", "\\thicksim");
-defineSymbol(math, ams, rel, "\u2248", "\\thickapprox");
-defineSymbol(math, ams, rel, "\u2ac6", "\\supseteqq");
-defineSymbol(math, ams, rel, "\u22d1", "\\Supset");
-defineSymbol(math, ams, rel, "\u2290", "\\sqsupset");
-defineSymbol(math, ams, rel, "\u227d", "\\succcurlyeq");
-defineSymbol(math, ams, rel, "\u22df", "\\curlyeqsucc");
-defineSymbol(math, ams, rel, "\u227f", "\\succsim");
-defineSymbol(math, ams, rel, "\u2ab8", "\\succapprox");
-defineSymbol(math, ams, rel, "\u22b3", "\\vartriangleright");
-defineSymbol(math, ams, rel, "\u22b5", "\\trianglerighteq");
-defineSymbol(math, ams, rel, "\u22a9", "\\Vdash");
-defineSymbol(math, ams, rel, "\u2223", "\\shortmid");
-defineSymbol(math, ams, rel, "\u2225", "\\shortparallel");
-defineSymbol(math, ams, rel, "\u226c", "\\between");
-defineSymbol(math, ams, rel, "\u22d4", "\\pitchfork");
-defineSymbol(math, ams, rel, "\u221d", "\\varpropto");
-defineSymbol(math, ams, rel, "\u25c0", "\\blacktriangleleft");
-defineSymbol(math, ams, rel, "\u2234", "\\therefore");
-defineSymbol(math, ams, rel, "\u220d", "\\backepsilon");
-defineSymbol(math, ams, rel, "\u25b6", "\\blacktriangleright");
-defineSymbol(math, ams, rel, "\u2235", "\\because");
-defineSymbol(math, ams, rel, "\u22d8", "\\llless");
-defineSymbol(math, ams, rel, "\u22d9", "\\gggtr");
-defineSymbol(math, ams, bin, "\u22b2", "\\lhd");
-defineSymbol(math, ams, bin, "\u22b3", "\\rhd");
-defineSymbol(math, ams, rel, "\u2242", "\\eqsim");
-defineSymbol(math, main, rel, "\u22c8", "\\Join");
-defineSymbol(math, ams, rel, "\u2251", "\\Doteq");
-
-// AMS Binary Operators
-defineSymbol(math, ams, bin, "\u2214", "\\dotplus");
-defineSymbol(math, ams, bin, "\u2216", "\\smallsetminus");
-defineSymbol(math, ams, bin, "\u22d2", "\\Cap");
-defineSymbol(math, ams, bin, "\u22d3", "\\Cup");
-defineSymbol(math, ams, bin, "\u2a5e", "\\doublebarwedge");
-defineSymbol(math, ams, bin, "\u229f", "\\boxminus");
-defineSymbol(math, ams, bin, "\u229e", "\\boxplus");
-defineSymbol(math, ams, bin, "\u22c7", "\\divideontimes");
-defineSymbol(math, ams, bin, "\u22c9", "\\ltimes");
-defineSymbol(math, ams, bin, "\u22ca", "\\rtimes");
-defineSymbol(math, ams, bin, "\u22cb", "\\leftthreetimes");
-defineSymbol(math, ams, bin, "\u22cc", "\\rightthreetimes");
-defineSymbol(math, ams, bin, "\u22cf", "\\curlywedge");
-defineSymbol(math, ams, bin, "\u22ce", "\\curlyvee");
-defineSymbol(math, ams, bin, "\u229d", "\\circleddash");
-defineSymbol(math, ams, bin, "\u229b", "\\circledast");
-defineSymbol(math, ams, bin, "\u22c5", "\\centerdot");
-defineSymbol(math, ams, bin, "\u22ba", "\\intercal");
-defineSymbol(math, ams, bin, "\u22d2", "\\doublecap");
-defineSymbol(math, ams, bin, "\u22d3", "\\doublecup");
-defineSymbol(math, ams, bin, "\u22a0", "\\boxtimes");
-
-// AMS Arrows
-defineSymbol(math, ams, rel, "\u21e2", "\\dashrightarrow");
-defineSymbol(math, ams, rel, "\u21e0", "\\dashleftarrow");
-defineSymbol(math, ams, rel, "\u21c7", "\\leftleftarrows");
-defineSymbol(math, ams, rel, "\u21c6", "\\leftrightarrows");
-defineSymbol(math, ams, rel, "\u21da", "\\Lleftarrow");
-defineSymbol(math, ams, rel, "\u219e", "\\twoheadleftarrow");
-defineSymbol(math, ams, rel, "\u21a2", "\\leftarrowtail");
-defineSymbol(math, ams, rel, "\u21ab", "\\looparrowleft");
-defineSymbol(math, ams, rel, "\u21cb", "\\leftrightharpoons");
-defineSymbol(math, ams, rel, "\u21b6", "\\curvearrowleft");
-defineSymbol(math, ams, rel, "\u21ba", "\\circlearrowleft");
-defineSymbol(math, ams, rel, "\u21b0", "\\Lsh");
-defineSymbol(math, ams, rel, "\u21c8", "\\upuparrows");
-defineSymbol(math, ams, rel, "\u21bf", "\\upharpoonleft");
-defineSymbol(math, ams, rel, "\u21c3", "\\downharpoonleft");
-defineSymbol(math, ams, rel, "\u22b8", "\\multimap");
-defineSymbol(math, ams, rel, "\u21ad", "\\leftrightsquigarrow");
-defineSymbol(math, ams, rel, "\u21c9", "\\rightrightarrows");
-defineSymbol(math, ams, rel, "\u21c4", "\\rightleftarrows");
-defineSymbol(math, ams, rel, "\u21a0", "\\twoheadrightarrow");
-defineSymbol(math, ams, rel, "\u21a3", "\\rightarrowtail");
-defineSymbol(math, ams, rel, "\u21ac", "\\looparrowright");
-defineSymbol(math, ams, rel, "\u21b7", "\\curvearrowright");
-defineSymbol(math, ams, rel, "\u21bb", "\\circlearrowright");
-defineSymbol(math, ams, rel, "\u21b1", "\\Rsh");
-defineSymbol(math, ams, rel, "\u21ca", "\\downdownarrows");
-defineSymbol(math, ams, rel, "\u21be", "\\upharpoonright");
-defineSymbol(math, ams, rel, "\u21c2", "\\downharpoonright");
-defineSymbol(math, ams, rel, "\u21dd", "\\rightsquigarrow");
-defineSymbol(math, ams, rel, "\u21dd", "\\leadsto");
-defineSymbol(math, ams, rel, "\u21db", "\\Rrightarrow");
-defineSymbol(math, ams, rel, "\u21be", "\\restriction");
-
-defineSymbol(math, main, textord, "\u2018", "`");
-defineSymbol(math, main, textord, "$", "\\$");
-defineSymbol(math, main, textord, "%", "\\%");
-defineSymbol(math, main, textord, "_", "\\_");
-defineSymbol(math, main, textord, "\u2220", "\\angle");
-defineSymbol(math, main, textord, "\u221e", "\\infty");
-defineSymbol(math, main, textord, "\u2032", "\\prime");
-defineSymbol(math, main, textord, "\u25b3", "\\triangle");
-defineSymbol(math, main, textord, "\u0393", "\\Gamma");
-defineSymbol(math, main, textord, "\u0394", "\\Delta");
-defineSymbol(math, main, textord, "\u0398", "\\Theta");
-defineSymbol(math, main, textord, "\u039b", "\\Lambda");
-defineSymbol(math, main, textord, "\u039e", "\\Xi");
-defineSymbol(math, main, textord, "\u03a0", "\\Pi");
-defineSymbol(math, main, textord, "\u03a3", "\\Sigma");
-defineSymbol(math, main, textord, "\u03a5", "\\Upsilon");
-defineSymbol(math, main, textord, "\u03a6", "\\Phi");
-defineSymbol(math, main, textord, "\u03a8", "\\Psi");
-defineSymbol(math, main, textord, "\u03a9", "\\Omega");
-defineSymbol(math, main, textord, "\u00ac", "\\neg");
-defineSymbol(math, main, textord, "\u00ac", "\\lnot");
-defineSymbol(math, main, textord, "\u22a4", "\\top");
-defineSymbol(math, main, textord, "\u22a5", "\\bot");
-defineSymbol(math, main, textord, "\u2205", "\\emptyset");
-defineSymbol(math, ams, textord, "\u2205", "\\varnothing");
-defineSymbol(math, main, mathord, "\u03b1", "\\alpha");
-defineSymbol(math, main, mathord, "\u03b2", "\\beta");
-defineSymbol(math, main, mathord, "\u03b3", "\\gamma");
-defineSymbol(math, main, mathord, "\u03b4", "\\delta");
-defineSymbol(math, main, mathord, "\u03f5", "\\epsilon");
-defineSymbol(math, main, mathord, "\u03b6", "\\zeta");
-defineSymbol(math, main, mathord, "\u03b7", "\\eta");
-defineSymbol(math, main, mathord, "\u03b8", "\\theta");
-defineSymbol(math, main, mathord, "\u03b9", "\\iota");
-defineSymbol(math, main, mathord, "\u03ba", "\\kappa");
-defineSymbol(math, main, mathord, "\u03bb", "\\lambda");
-defineSymbol(math, main, mathord, "\u03bc", "\\mu");
-defineSymbol(math, main, mathord, "\u03bd", "\\nu");
-defineSymbol(math, main, mathord, "\u03be", "\\xi");
-defineSymbol(math, main, mathord, "o", "\\omicron");
-defineSymbol(math, main, mathord, "\u03c0", "\\pi");
-defineSymbol(math, main, mathord, "\u03c1", "\\rho");
-defineSymbol(math, main, mathord, "\u03c3", "\\sigma");
-defineSymbol(math, main, mathord, "\u03c4", "\\tau");
-defineSymbol(math, main, mathord, "\u03c5", "\\upsilon");
-defineSymbol(math, main, mathord, "\u03d5", "\\phi");
-defineSymbol(math, main, mathord, "\u03c7", "\\chi");
-defineSymbol(math, main, mathord, "\u03c8", "\\psi");
-defineSymbol(math, main, mathord, "\u03c9", "\\omega");
-defineSymbol(math, main, mathord, "\u03b5", "\\varepsilon");
-defineSymbol(math, main, mathord, "\u03d1", "\\vartheta");
-defineSymbol(math, main, mathord, "\u03d6", "\\varpi");
-defineSymbol(math, main, mathord, "\u03f1", "\\varrho");
-defineSymbol(math, main, mathord, "\u03c2", "\\varsigma");
-defineSymbol(math, main, mathord, "\u03c6", "\\varphi");
-defineSymbol(math, main, bin, "\u2217", "*");
-defineSymbol(math, main, bin, "+", "+");
-defineSymbol(math, main, bin, "\u2212", "-");
-defineSymbol(math, main, bin, "\u22c5", "\\cdot");
-defineSymbol(math, main, bin, "\u2218", "\\circ");
-defineSymbol(math, main, bin, "\u00f7", "\\div");
-defineSymbol(math, main, bin, "\u00b1", "\\pm");
-defineSymbol(math, main, bin, "\u00d7", "\\times");
-defineSymbol(math, main, bin, "\u2229", "\\cap");
-defineSymbol(math, main, bin, "\u222a", "\\cup");
-defineSymbol(math, main, bin, "\u2216", "\\setminus");
-defineSymbol(math, main, bin, "\u2227", "\\land");
-defineSymbol(math, main, bin, "\u2228", "\\lor");
-defineSymbol(math, main, bin, "\u2227", "\\wedge");
-defineSymbol(math, main, bin, "\u2228", "\\vee");
-defineSymbol(math, main, textord, "\u221a", "\\surd");
-defineSymbol(math, main, open, "(", "(");
-defineSymbol(math, main, open, "[", "[");
-defineSymbol(math, main, open, "\u27e8", "\\langle");
-defineSymbol(math, main, open, "\u2223", "\\lvert");
-defineSymbol(math, main, open, "\u2225", "\\lVert");
-defineSymbol(math, main, close, ")", ")");
-defineSymbol(math, main, close, "]", "]");
-defineSymbol(math, main, close, "?", "?");
-defineSymbol(math, main, close, "!", "!");
-defineSymbol(math, main, close, "\u27e9", "\\rangle");
-defineSymbol(math, main, close, "\u2223", "\\rvert");
-defineSymbol(math, main, close, "\u2225", "\\rVert");
-defineSymbol(math, main, rel, "=", "=");
-defineSymbol(math, main, rel, "<", "<");
-defineSymbol(math, main, rel, ">", ">");
-defineSymbol(math, main, rel, ":", ":");
-defineSymbol(math, main, rel, "\u2248", "\\approx");
-defineSymbol(math, main, rel, "\u2245", "\\cong");
-defineSymbol(math, main, rel, "\u2265", "\\ge");
-defineSymbol(math, main, rel, "\u2265", "\\geq");
-defineSymbol(math, main, rel, "\u2190", "\\gets");
-defineSymbol(math, main, rel, ">", "\\gt");
-defineSymbol(math, main, rel, "\u2208", "\\in");
-defineSymbol(math, main, rel, "\u2209", "\\notin");
-defineSymbol(math, main, rel, "\u2282", "\\subset");
-defineSymbol(math, main, rel, "\u2283", "\\supset");
-defineSymbol(math, main, rel, "\u2286", "\\subseteq");
-defineSymbol(math, main, rel, "\u2287", "\\supseteq");
-defineSymbol(math, ams, rel, "\u2288", "\\nsubseteq");
-defineSymbol(math, ams, rel, "\u2289", "\\nsupseteq");
-defineSymbol(math, main, rel, "\u22a8", "\\models");
-defineSymbol(math, main, rel, "\u2190", "\\leftarrow");
-defineSymbol(math, main, rel, "\u2264", "\\le");
-defineSymbol(math, main, rel, "\u2264", "\\leq");
-defineSymbol(math, main, rel, "<", "\\lt");
-defineSymbol(math, main, rel, "\u2260", "\\ne");
-defineSymbol(math, main, rel, "\u2260", "\\neq");
-defineSymbol(math, main, rel, "\u2192", "\\rightarrow");
-defineSymbol(math, main, rel, "\u2192", "\\to");
-defineSymbol(math, ams, rel, "\u2271", "\\ngeq");
-defineSymbol(math, ams, rel, "\u2270", "\\nleq");
-defineSymbol(math, main, spacing, null, "\\!");
-defineSymbol(math, main, spacing, "\u00a0", "\\ ");
-defineSymbol(math, main, spacing, "\u00a0", "~");
-defineSymbol(math, main, spacing, null, "\\,");
-defineSymbol(math, main, spacing, null, "\\:");
-defineSymbol(math, main, spacing, null, "\\;");
-defineSymbol(math, main, spacing, null, "\\enspace");
-defineSymbol(math, main, spacing, null, "\\qquad");
-defineSymbol(math, main, spacing, null, "\\quad");
-defineSymbol(math, main, spacing, "\u00a0", "\\space");
-defineSymbol(math, main, punct, ",", ",");
-defineSymbol(math, main, punct, ";", ";");
-defineSymbol(math, main, punct, ":", "\\colon");
-defineSymbol(math, ams, bin, "\u22bc", "\\barwedge");
-defineSymbol(math, ams, bin, "\u22bb", "\\veebar");
-defineSymbol(math, main, bin, "\u2299", "\\odot");
-defineSymbol(math, main, bin, "\u2295", "\\oplus");
-defineSymbol(math, main, bin, "\u2297", "\\otimes");
-defineSymbol(math, main, textord, "\u2202", "\\partial");
-defineSymbol(math, main, bin, "\u2298", "\\oslash");
-defineSymbol(math, ams, bin, "\u229a", "\\circledcirc");
-defineSymbol(math, ams, bin, "\u22a1", "\\boxdot");
-defineSymbol(math, main, bin, "\u25b3", "\\bigtriangleup");
-defineSymbol(math, main, bin, "\u25bd", "\\bigtriangledown");
-defineSymbol(math, main, bin, "\u2020", "\\dagger");
-defineSymbol(math, main, bin, "\u22c4", "\\diamond");
-defineSymbol(math, main, bin, "\u22c6", "\\star");
-defineSymbol(math, main, bin, "\u25c3", "\\triangleleft");
-defineSymbol(math, main, bin, "\u25b9", "\\triangleright");
-defineSymbol(math, main, open, "{", "\\{");
-defineSymbol(math, main, close, "}", "\\}");
-defineSymbol(math, main, open, "{", "\\lbrace");
-defineSymbol(math, main, close, "}", "\\rbrace");
-defineSymbol(math, main, open, "[", "\\lbrack");
-defineSymbol(math, main, close, "]", "\\rbrack");
-defineSymbol(math, main, open, "\u230a", "\\lfloor");
-defineSymbol(math, main, close, "\u230b", "\\rfloor");
-defineSymbol(math, main, open, "\u2308", "\\lceil");
-defineSymbol(math, main, close, "\u2309", "\\rceil");
-defineSymbol(math, main, textord, "\\", "\\backslash");
-defineSymbol(math, main, textord, "\u2223", "|");
-defineSymbol(math, main, textord, "\u2223", "\\vert");
-defineSymbol(math, main, textord, "\u2225", "\\|");
-defineSymbol(math, main, textord, "\u2225", "\\Vert");
-defineSymbol(math, main, rel, "\u2191", "\\uparrow");
-defineSymbol(math, main, rel, "\u21d1", "\\Uparrow");
-defineSymbol(math, main, rel, "\u2193", "\\downarrow");
-defineSymbol(math, main, rel, "\u21d3", "\\Downarrow");
-defineSymbol(math, main, rel, "\u2195", "\\updownarrow");
-defineSymbol(math, main, rel, "\u21d5", "\\Updownarrow");
-defineSymbol(math, math, op, "\u2210", "\\coprod");
-defineSymbol(math, math, op, "\u22c1", "\\bigvee");
-defineSymbol(math, math, op, "\u22c0", "\\bigwedge");
-defineSymbol(math, math, op, "\u2a04", "\\biguplus");
-defineSymbol(math, math, op, "\u22c2", "\\bigcap");
-defineSymbol(math, math, op, "\u22c3", "\\bigcup");
-defineSymbol(math, math, op, "\u222b", "\\int");
-defineSymbol(math, math, op, "\u222b", "\\intop");
-defineSymbol(math, math, op, "\u222c", "\\iint");
-defineSymbol(math, math, op, "\u222d", "\\iiint");
-defineSymbol(math, math, op, "\u220f", "\\prod");
-defineSymbol(math, math, op, "\u2211", "\\sum");
-defineSymbol(math, math, op, "\u2a02", "\\bigotimes");
-defineSymbol(math, math, op, "\u2a01", "\\bigoplus");
-defineSymbol(math, math, op, "\u2a00", "\\bigodot");
-defineSymbol(math, math, op, "\u222e", "\\oint");
-defineSymbol(math, math, op, "\u2a06", "\\bigsqcup");
-defineSymbol(math, math, op, "\u222b", "\\smallint");
-defineSymbol(math, main, inner, "\u2026", "\\ldots");
-defineSymbol(math, main, inner, "\u22ef", "\\cdots");
-defineSymbol(math, main, inner, "\u22f1", "\\ddots");
-defineSymbol(math, main, textord, "\u22ee", "\\vdots");
-defineSymbol(math, main, accent, "\u00b4", "\\acute");
-defineSymbol(math, main, accent, "\u0060", "\\grave");
-defineSymbol(math, main, accent, "\u00a8", "\\ddot");
-defineSymbol(math, main, accent, "\u007e", "\\tilde");
-defineSymbol(math, main, accent, "\u00af", "\\bar");
-defineSymbol(math, main, accent, "\u02d8", "\\breve");
-defineSymbol(math, main, accent, "\u02c7", "\\check");
-defineSymbol(math, main, accent, "\u005e", "\\hat");
-defineSymbol(math, main, accent, "\u20d7", "\\vec");
-defineSymbol(math, main, accent, "\u02d9", "\\dot");
-defineSymbol(math, main, mathord, "\u0131", "\\imath");
-defineSymbol(math, main, mathord, "\u0237", "\\jmath");
-
-defineSymbol(text, main, textord, "\u2013", "--");
-defineSymbol(text, main, textord, "\u2014", "---");
-defineSymbol(text, main, textord, "\u2018", "`");
-defineSymbol(text, main, textord, "\u2019", "'");
-defineSymbol(text, main, textord, "\u201c", "``");
-defineSymbol(text, main, textord, "\u201d", "''");
-defineSymbol(math, main, textord, "\u00b0", "\\degree");
-defineSymbol(text, main, textord, "\u00b0", "\\degree");
-defineSymbol(math, main, mathord, "\u00a3", "\\pounds");
-defineSymbol(math, ams, textord, "\u2720", "\\maltese");
-defineSymbol(text, ams, textord, "\u2720", "\\maltese");
-
-defineSymbol(text, main, spacing, "\u00a0", "\\ ");
-defineSymbol(text, main, spacing, "\u00a0", " ");
-defineSymbol(text, main, spacing, "\u00a0", "~");
-
-// There are lots of symbols which are the same, so we add them in afterwards.
-var i;
-var ch;
-
-// All of these are textords in math mode
-var mathTextSymbols = "0123456789/@.\"";
-for (i = 0; i < mathTextSymbols.length; i++) {
- ch = mathTextSymbols.charAt(i);
- defineSymbol(math, main, textord, ch, ch);
-}
-
-// All of these are textords in text mode
-var textSymbols = "0123456789!@*()-=+[]\";:?/.,";
-for (i = 0; i < textSymbols.length; i++) {
- ch = textSymbols.charAt(i);
- defineSymbol(text, main, textord, ch, ch);
-}
-
-// All of these are textords in text mode, and mathords in math mode
-var letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-for (i = 0; i < letters.length; i++) {
- ch = letters.charAt(i);
- defineSymbol(math, main, mathord, ch, ch);
- defineSymbol(text, main, textord, ch, ch);
-}
-
-// Latin-1 letters
-for (i = 0x00C0; i <= 0x00D6; i++) {
- ch = String.fromCharCode(i);
- defineSymbol(text, main, textord, ch, ch);
-}
-
-for (i = 0x00D8; i <= 0x00F6; i++) {
- ch = String.fromCharCode(i);
- defineSymbol(text, main, textord, ch, ch);
-}
-
-for (i = 0x00F8; i <= 0x00FF; i++) {
- ch = String.fromCharCode(i);
- defineSymbol(text, main, textord, ch, ch);
-}
-
-// Cyrillic
-for (i = 0x0410; i <= 0x044F; i++) {
- ch = String.fromCharCode(i);
- defineSymbol(text, main, textord, ch, ch);
-}
-
-},{}],24:[function(require,module,exports){
-var hangulRegex = /[\uAC00-\uD7AF]/;
-
-// This regex combines
-// - Hiragana: [\u3040-\u309F]
-// - Katakana: [\u30A0-\u30FF]
-// - CJK ideograms: [\u4E00-\u9FAF]
-// - Hangul syllables: [\uAC00-\uD7AF]
-// Notably missing are halfwidth Katakana and Romanji glyphs.
-var cjkRegex =
- /[\u3040-\u309F]|[\u30A0-\u30FF]|[\u4E00-\u9FAF]|[\uAC00-\uD7AF]/;
-
-module.exports = {
- cjkRegex: cjkRegex,
- hangulRegex: hangulRegex,
-};
-
-},{}],25:[function(require,module,exports){
-/**
- * This file contains a list of utility functions which are useful in other
- * files.
- */
-
-/**
- * Provide an `indexOf` function which works in IE8, but defers to native if
- * possible.
- */
-var nativeIndexOf = Array.prototype.indexOf;
-var indexOf = function(list, elem) {
- if (list == null) {
- return -1;
- }
- if (nativeIndexOf && list.indexOf === nativeIndexOf) {
- return list.indexOf(elem);
- }
- var i = 0;
- var l = list.length;
- for (; i < l; i++) {
- if (list[i] === elem) {
- return i;
- }
- }
- return -1;
-};
-
-/**
- * Return whether an element is contained in a list
- */
-var contains = function(list, elem) {
- return indexOf(list, elem) !== -1;
-};
-
-/**
- * Provide a default value if a setting is undefined
- */
-var deflt = function(setting, defaultIfUndefined) {
- return setting === undefined ? defaultIfUndefined : setting;
-};
-
-// hyphenate and escape adapted from Facebook's React under Apache 2 license
-
-var uppercase = /([A-Z])/g;
-var hyphenate = function(str) {
- return str.replace(uppercase, "-$1").toLowerCase();
-};
-
-var ESCAPE_LOOKUP = {
- "&": "&amp;",
- ">": "&gt;",
- "<": "&lt;",
- "\"": "&quot;",
- "'": "&#x27;",
-};
-
-var ESCAPE_REGEX = /[&><"']/g;
-
-function escaper(match) {
- return ESCAPE_LOOKUP[match];
-}
-
-/**
- * Escapes text to prevent scripting attacks.
- *
- * @param {*} text Text value to escape.
- * @return {string} An escaped string.
- */
-function escape(text) {
- return ("" + text).replace(ESCAPE_REGEX, escaper);
-}
-
-/**
- * A function to set the text content of a DOM element in all supported
- * browsers. Note that we don't define this if there is no document.
- */
-var setTextContent;
-if (typeof document !== "undefined") {
- var testNode = document.createElement("span");
- if ("textContent" in testNode) {
- setTextContent = function(node, text) {
- node.textContent = text;
- };
- } else {
- setTextContent = function(node, text) {
- node.innerText = text;
- };
- }
-}
-
-/**
- * A function to clear a node.
- */
-function clearNode(node) {
- setTextContent(node, "");
-}
-
-module.exports = {
- contains: contains,
- deflt: deflt,
- escape: escape,
- hyphenate: hyphenate,
- indexOf: indexOf,
- setTextContent: setTextContent,
- clearNode: clearNode,
-};
-
-},{}]},{},[1])(1)
-}); \ No newline at end of file
diff --git a/vendor/assets/javascripts/peek.js b/vendor/assets/javascripts/peek.js
index 6a341a3f0fe..695eeb27c17 100644
--- a/vendor/assets/javascripts/peek.js
+++ b/vendor/assets/javascripts/peek.js
@@ -10,7 +10,7 @@
(function($) {
var fetchRequestResults, getRequestId, peekEnabled, updatePerformanceBar;
getRequestId = function() {
- return $('#peek').data('request-id');
+ return $('#peek').data('requestId');
};
peekEnabled = function() {
return $('#peek').length;
diff --git a/vendor/assets/stylesheets/katex.scss b/vendor/assets/stylesheets/katex.scss
deleted file mode 100644
index b45836716f2..00000000000
--- a/vendor/assets/stylesheets/katex.scss
+++ /dev/null
@@ -1,977 +0,0 @@
-/*
-The MIT License (MIT)
-
-Copyright (c) 2015 Khan Academy
-
-This software also uses portions of the underscore.js project, which is
-MIT licensed with the following copyright:
-
-Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
-Reporters & Editors
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-/*
- Here is how to build a version of KaTeX that works with gitlab.
-
- The problem is that the standard procedure for changing font location doesn't work for the empty string.
-
- 1. Clone KaTeX. Anything later than 4fb9445a9 (is merged into master) will do.
- 2. make (requires node)
- 3. sed -e 's,fonts/,,' -e 's/url\(([^)]*)\)/url(font-path\1)/g' build/katex.css > build/katex.scss
- 4. Copy build/katex.js to gitlab/vendor/assets/javascripts/katex.js,
- build/katex.scss to gitlab/vendor/assets/stylesheets/katex.scss and
- fonts/* to gitlab/vendor/assets/fonts/.
-*/
-
-@font-face {
- font-family: 'KaTeX_AMS';
- src: url(font-path('KaTeX_AMS-Regular.eot'));
- src: url(font-path('KaTeX_AMS-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_AMS-Regular.woff2')) format('woff2'), url(font-path('KaTeX_AMS-Regular.woff')) format('woff'), url(font-path('KaTeX_AMS-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Caligraphic';
- src: url(font-path('KaTeX_Caligraphic-Bold.eot'));
- src: url(font-path('KaTeX_Caligraphic-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Caligraphic-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Caligraphic-Bold.woff')) format('woff'), url(font-path('KaTeX_Caligraphic-Bold.ttf')) format('truetype');
- font-weight: 600;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Caligraphic';
- src: url(font-path('KaTeX_Caligraphic-Regular.eot'));
- src: url(font-path('KaTeX_Caligraphic-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Caligraphic-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Caligraphic-Regular.woff')) format('woff'), url(font-path('KaTeX_Caligraphic-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Fraktur';
- src: url(font-path('KaTeX_Fraktur-Bold.eot'));
- src: url(font-path('KaTeX_Fraktur-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Fraktur-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Fraktur-Bold.woff')) format('woff'), url(font-path('KaTeX_Fraktur-Bold.ttf')) format('truetype');
- font-weight: 600;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Fraktur';
- src: url(font-path('KaTeX_Fraktur-Regular.eot'));
- src: url(font-path('KaTeX_Fraktur-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Fraktur-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Fraktur-Regular.woff')) format('woff'), url(font-path('KaTeX_Fraktur-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Main';
- src: url(font-path('KaTeX_Main-Bold.eot'));
- src: url(font-path('KaTeX_Main-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Main-Bold.woff')) format('woff'), url(font-path('KaTeX_Main-Bold.ttf')) format('truetype');
- font-weight: 600;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Main';
- src: url(font-path('KaTeX_Main-Italic.eot'));
- src: url(font-path('KaTeX_Main-Italic.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Italic.woff2')) format('woff2'), url(font-path('KaTeX_Main-Italic.woff')) format('woff'), url(font-path('KaTeX_Main-Italic.ttf')) format('truetype');
- font-weight: 400;
- font-style: italic;
-}
-@font-face {
- font-family: 'KaTeX_Main';
- src: url(font-path('KaTeX_Main-Regular.eot'));
- src: url(font-path('KaTeX_Main-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Main-Regular.woff')) format('woff'), url(font-path('KaTeX_Main-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Math';
- src: url(font-path('KaTeX_Math-Italic.eot'));
- src: url(font-path('KaTeX_Math-Italic.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Math-Italic.woff2')) format('woff2'), url(font-path('KaTeX_Math-Italic.woff')) format('woff'), url(font-path('KaTeX_Math-Italic.ttf')) format('truetype');
- font-weight: 400;
- font-style: italic;
-}
-@font-face {
- font-family: 'KaTeX_SansSerif';
- src: url(font-path('KaTeX_SansSerif-Regular.eot'));
- src: url(font-path('KaTeX_SansSerif-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_SansSerif-Regular.woff2')) format('woff2'), url(font-path('KaTeX_SansSerif-Regular.woff')) format('woff'), url(font-path('KaTeX_SansSerif-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Script';
- src: url(font-path('KaTeX_Script-Regular.eot'));
- src: url(font-path('KaTeX_Script-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Script-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Script-Regular.woff')) format('woff'), url(font-path('KaTeX_Script-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Size1';
- src: url(font-path('KaTeX_Size1-Regular.eot'));
- src: url(font-path('KaTeX_Size1-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size1-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size1-Regular.woff')) format('woff'), url(font-path('KaTeX_Size1-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Size2';
- src: url(font-path('KaTeX_Size2-Regular.eot'));
- src: url(font-path('KaTeX_Size2-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size2-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size2-Regular.woff')) format('woff'), url(font-path('KaTeX_Size2-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Size3';
- src: url(font-path('KaTeX_Size3-Regular.eot'));
- src: url(font-path('KaTeX_Size3-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size3-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size3-Regular.woff')) format('woff'), url(font-path('KaTeX_Size3-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Size4';
- src: url(font-path('KaTeX_Size4-Regular.eot'));
- src: url(font-path('KaTeX_Size4-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size4-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size4-Regular.woff')) format('woff'), url(font-path('KaTeX_Size4-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-@font-face {
- font-family: 'KaTeX_Typewriter';
- src: url(font-path('KaTeX_Typewriter-Regular.eot'));
- src: url(font-path('KaTeX_Typewriter-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Typewriter-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Typewriter-Regular.woff')) format('woff'), url(font-path('KaTeX_Typewriter-Regular.ttf')) format('truetype');
- font-weight: 400;
- font-style: normal;
-}
-.katex-display {
- display: block;
- margin: 1em 0;
- text-align: center;
-}
-.katex-display > .katex {
- display: inline-block;
- text-align: initial;
-}
-.katex {
- font: normal 1.21em KaTeX_Main, Times New Roman, serif;
- line-height: 1.2;
- white-space: nowrap;
- text-indent: 0;
-}
-.katex .katex-html {
- display: inline-block;
-}
-.katex .katex-mathml {
- position: absolute;
- clip: rect(1px, 1px, 1px, 1px);
- padding: 0;
- border: 0;
- height: 1px;
- width: 1px;
- overflow: hidden;
-}
-.katex .base {
- display: inline-block;
-}
-.katex .strut {
- display: inline-block;
-}
-.katex .mathit {
- font-family: KaTeX_Math;
- font-style: italic;
-}
-.katex .mathbf {
- font-family: KaTeX_Main;
- font-weight: 600;
-}
-.katex .amsrm {
- font-family: KaTeX_AMS;
-}
-.katex .mathbb {
- font-family: KaTeX_AMS;
-}
-.katex .mathcal {
- font-family: KaTeX_Caligraphic;
-}
-.katex .mathfrak {
- font-family: KaTeX_Fraktur;
-}
-.katex .mathtt {
- font-family: KaTeX_Typewriter;
-}
-.katex .mathscr {
- font-family: KaTeX_Script;
-}
-.katex .mathsf {
- font-family: KaTeX_SansSerif;
-}
-.katex .mainit {
- font-family: KaTeX_Main;
- font-style: italic;
-}
-.katex .textstyle > .mord + .mop {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mord + .mbin {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .mord + .mrel {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mord + .minner {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mop + .mord {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mop + .mop {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mop + .mrel {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mop + .minner {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mbin + .mord {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .mbin + .mop {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .mbin + .mopen {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .mbin + .minner {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .mrel + .mord {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mrel + .mop {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mrel + .mopen {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mrel + .minner {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mclose + .mop {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mclose + .mbin {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .mclose + .mrel {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .mclose + .minner {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .mord {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .mop {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .mrel {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .mopen {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .mclose {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .mpunct {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .mpunct + .minner {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .minner + .mord {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .minner + .mop {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .minner + .mbin {
- margin-left: 0.22222em;
-}
-.katex .textstyle > .minner + .mrel {
- margin-left: 0.27778em;
-}
-.katex .textstyle > .minner + .mopen {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .minner + .mpunct {
- margin-left: 0.16667em;
-}
-.katex .textstyle > .minner + .minner {
- margin-left: 0.16667em;
-}
-.katex .mord + .mop {
- margin-left: 0.16667em;
-}
-.katex .mop + .mord {
- margin-left: 0.16667em;
-}
-.katex .mop + .mop {
- margin-left: 0.16667em;
-}
-.katex .mclose + .mop {
- margin-left: 0.16667em;
-}
-.katex .minner + .mop {
- margin-left: 0.16667em;
-}
-.katex .reset-textstyle.textstyle {
- font-size: 1em;
-}
-.katex .reset-textstyle.scriptstyle {
- font-size: 0.7em;
-}
-.katex .reset-textstyle.scriptscriptstyle {
- font-size: 0.5em;
-}
-.katex .reset-scriptstyle.textstyle {
- font-size: 1.42857em;
-}
-.katex .reset-scriptstyle.scriptstyle {
- font-size: 1em;
-}
-.katex .reset-scriptstyle.scriptscriptstyle {
- font-size: 0.71429em;
-}
-.katex .reset-scriptscriptstyle.textstyle {
- font-size: 2em;
-}
-.katex .reset-scriptscriptstyle.scriptstyle {
- font-size: 1.4em;
-}
-.katex .reset-scriptscriptstyle.scriptscriptstyle {
- font-size: 1em;
-}
-.katex .style-wrap {
- position: relative;
-}
-.katex .vlist {
- display: inline-block;
-}
-.katex .vlist > span {
- display: block;
- height: 0;
- position: relative;
-}
-.katex .vlist > span > span {
- display: inline-block;
-}
-.katex .vlist .baseline-fix {
- display: inline-table;
- table-layout: fixed;
-}
-.katex .msupsub {
- text-align: left;
-}
-.katex .mfrac > span > span {
- text-align: center;
-}
-.katex .mfrac .frac-line {
- width: 100%;
-}
-.katex .mfrac .frac-line:before {
- border-bottom-style: solid;
- border-bottom-width: 1px;
- content: "";
- display: block;
-}
-.katex .mfrac .frac-line:after {
- border-bottom-style: solid;
- border-bottom-width: 0.04em;
- content: "";
- display: block;
- margin-top: -1px;
-}
-.katex .mspace {
- display: inline-block;
-}
-.katex .mspace.negativethinspace {
- margin-left: -0.16667em;
-}
-.katex .mspace.thinspace {
- width: 0.16667em;
-}
-.katex .mspace.mediumspace {
- width: 0.22222em;
-}
-.katex .mspace.thickspace {
- width: 0.27778em;
-}
-.katex .mspace.enspace {
- width: 0.5em;
-}
-.katex .mspace.quad {
- width: 1em;
-}
-.katex .mspace.qquad {
- width: 2em;
-}
-.katex .llap,
-.katex .rlap {
- width: 0;
- position: relative;
-}
-.katex .llap > .inner,
-.katex .rlap > .inner {
- position: absolute;
-}
-.katex .llap > .fix,
-.katex .rlap > .fix {
- display: inline-block;
-}
-.katex .llap > .inner {
- right: 0;
-}
-.katex .rlap > .inner {
- left: 0;
-}
-.katex .katex-logo .a {
- font-size: 0.75em;
- margin-left: -0.32em;
- position: relative;
- top: -0.2em;
-}
-.katex .katex-logo .t {
- margin-left: -0.23em;
-}
-.katex .katex-logo .e {
- margin-left: -0.1667em;
- position: relative;
- top: 0.2155em;
-}
-.katex .katex-logo .x {
- margin-left: -0.125em;
-}
-.katex .rule {
- display: inline-block;
- border: solid 0;
- position: relative;
-}
-.katex .overline .overline-line,
-.katex .underline .underline-line {
- width: 100%;
-}
-.katex .overline .overline-line:before,
-.katex .underline .underline-line:before {
- border-bottom-style: solid;
- border-bottom-width: 1px;
- content: "";
- display: block;
-}
-.katex .overline .overline-line:after,
-.katex .underline .underline-line:after {
- border-bottom-style: solid;
- border-bottom-width: 0.04em;
- content: "";
- display: block;
- margin-top: -1px;
-}
-.katex .sqrt > .sqrt-sign {
- position: relative;
-}
-.katex .sqrt .sqrt-line {
- width: 100%;
-}
-.katex .sqrt .sqrt-line:before {
- border-bottom-style: solid;
- border-bottom-width: 1px;
- content: "";
- display: block;
-}
-.katex .sqrt .sqrt-line:after {
- border-bottom-style: solid;
- border-bottom-width: 0.04em;
- content: "";
- display: block;
- margin-top: -1px;
-}
-.katex .sqrt > .root {
- margin-left: 0.27777778em;
- margin-right: -0.55555556em;
-}
-.katex .sizing,
-.katex .fontsize-ensurer {
- display: inline-block;
-}
-.katex .sizing.reset-size1.size1,
-.katex .fontsize-ensurer.reset-size1.size1 {
- font-size: 1em;
-}
-.katex .sizing.reset-size1.size2,
-.katex .fontsize-ensurer.reset-size1.size2 {
- font-size: 1.4em;
-}
-.katex .sizing.reset-size1.size3,
-.katex .fontsize-ensurer.reset-size1.size3 {
- font-size: 1.6em;
-}
-.katex .sizing.reset-size1.size4,
-.katex .fontsize-ensurer.reset-size1.size4 {
- font-size: 1.8em;
-}
-.katex .sizing.reset-size1.size5,
-.katex .fontsize-ensurer.reset-size1.size5 {
- font-size: 2em;
-}
-.katex .sizing.reset-size1.size6,
-.katex .fontsize-ensurer.reset-size1.size6 {
- font-size: 2.4em;
-}
-.katex .sizing.reset-size1.size7,
-.katex .fontsize-ensurer.reset-size1.size7 {
- font-size: 2.88em;
-}
-.katex .sizing.reset-size1.size8,
-.katex .fontsize-ensurer.reset-size1.size8 {
- font-size: 3.46em;
-}
-.katex .sizing.reset-size1.size9,
-.katex .fontsize-ensurer.reset-size1.size9 {
- font-size: 4.14em;
-}
-.katex .sizing.reset-size1.size10,
-.katex .fontsize-ensurer.reset-size1.size10 {
- font-size: 4.98em;
-}
-.katex .sizing.reset-size2.size1,
-.katex .fontsize-ensurer.reset-size2.size1 {
- font-size: 0.71428571em;
-}
-.katex .sizing.reset-size2.size2,
-.katex .fontsize-ensurer.reset-size2.size2 {
- font-size: 1em;
-}
-.katex .sizing.reset-size2.size3,
-.katex .fontsize-ensurer.reset-size2.size3 {
- font-size: 1.14285714em;
-}
-.katex .sizing.reset-size2.size4,
-.katex .fontsize-ensurer.reset-size2.size4 {
- font-size: 1.28571429em;
-}
-.katex .sizing.reset-size2.size5,
-.katex .fontsize-ensurer.reset-size2.size5 {
- font-size: 1.42857143em;
-}
-.katex .sizing.reset-size2.size6,
-.katex .fontsize-ensurer.reset-size2.size6 {
- font-size: 1.71428571em;
-}
-.katex .sizing.reset-size2.size7,
-.katex .fontsize-ensurer.reset-size2.size7 {
- font-size: 2.05714286em;
-}
-.katex .sizing.reset-size2.size8,
-.katex .fontsize-ensurer.reset-size2.size8 {
- font-size: 2.47142857em;
-}
-.katex .sizing.reset-size2.size9,
-.katex .fontsize-ensurer.reset-size2.size9 {
- font-size: 2.95714286em;
-}
-.katex .sizing.reset-size2.size10,
-.katex .fontsize-ensurer.reset-size2.size10 {
- font-size: 3.55714286em;
-}
-.katex .sizing.reset-size3.size1,
-.katex .fontsize-ensurer.reset-size3.size1 {
- font-size: 0.625em;
-}
-.katex .sizing.reset-size3.size2,
-.katex .fontsize-ensurer.reset-size3.size2 {
- font-size: 0.875em;
-}
-.katex .sizing.reset-size3.size3,
-.katex .fontsize-ensurer.reset-size3.size3 {
- font-size: 1em;
-}
-.katex .sizing.reset-size3.size4,
-.katex .fontsize-ensurer.reset-size3.size4 {
- font-size: 1.125em;
-}
-.katex .sizing.reset-size3.size5,
-.katex .fontsize-ensurer.reset-size3.size5 {
- font-size: 1.25em;
-}
-.katex .sizing.reset-size3.size6,
-.katex .fontsize-ensurer.reset-size3.size6 {
- font-size: 1.5em;
-}
-.katex .sizing.reset-size3.size7,
-.katex .fontsize-ensurer.reset-size3.size7 {
- font-size: 1.8em;
-}
-.katex .sizing.reset-size3.size8,
-.katex .fontsize-ensurer.reset-size3.size8 {
- font-size: 2.1625em;
-}
-.katex .sizing.reset-size3.size9,
-.katex .fontsize-ensurer.reset-size3.size9 {
- font-size: 2.5875em;
-}
-.katex .sizing.reset-size3.size10,
-.katex .fontsize-ensurer.reset-size3.size10 {
- font-size: 3.1125em;
-}
-.katex .sizing.reset-size4.size1,
-.katex .fontsize-ensurer.reset-size4.size1 {
- font-size: 0.55555556em;
-}
-.katex .sizing.reset-size4.size2,
-.katex .fontsize-ensurer.reset-size4.size2 {
- font-size: 0.77777778em;
-}
-.katex .sizing.reset-size4.size3,
-.katex .fontsize-ensurer.reset-size4.size3 {
- font-size: 0.88888889em;
-}
-.katex .sizing.reset-size4.size4,
-.katex .fontsize-ensurer.reset-size4.size4 {
- font-size: 1em;
-}
-.katex .sizing.reset-size4.size5,
-.katex .fontsize-ensurer.reset-size4.size5 {
- font-size: 1.11111111em;
-}
-.katex .sizing.reset-size4.size6,
-.katex .fontsize-ensurer.reset-size4.size6 {
- font-size: 1.33333333em;
-}
-.katex .sizing.reset-size4.size7,
-.katex .fontsize-ensurer.reset-size4.size7 {
- font-size: 1.6em;
-}
-.katex .sizing.reset-size4.size8,
-.katex .fontsize-ensurer.reset-size4.size8 {
- font-size: 1.92222222em;
-}
-.katex .sizing.reset-size4.size9,
-.katex .fontsize-ensurer.reset-size4.size9 {
- font-size: 2.3em;
-}
-.katex .sizing.reset-size4.size10,
-.katex .fontsize-ensurer.reset-size4.size10 {
- font-size: 2.76666667em;
-}
-.katex .sizing.reset-size5.size1,
-.katex .fontsize-ensurer.reset-size5.size1 {
- font-size: 0.5em;
-}
-.katex .sizing.reset-size5.size2,
-.katex .fontsize-ensurer.reset-size5.size2 {
- font-size: 0.7em;
-}
-.katex .sizing.reset-size5.size3,
-.katex .fontsize-ensurer.reset-size5.size3 {
- font-size: 0.8em;
-}
-.katex .sizing.reset-size5.size4,
-.katex .fontsize-ensurer.reset-size5.size4 {
- font-size: 0.9em;
-}
-.katex .sizing.reset-size5.size5,
-.katex .fontsize-ensurer.reset-size5.size5 {
- font-size: 1em;
-}
-.katex .sizing.reset-size5.size6,
-.katex .fontsize-ensurer.reset-size5.size6 {
- font-size: 1.2em;
-}
-.katex .sizing.reset-size5.size7,
-.katex .fontsize-ensurer.reset-size5.size7 {
- font-size: 1.44em;
-}
-.katex .sizing.reset-size5.size8,
-.katex .fontsize-ensurer.reset-size5.size8 {
- font-size: 1.73em;
-}
-.katex .sizing.reset-size5.size9,
-.katex .fontsize-ensurer.reset-size5.size9 {
- font-size: 2.07em;
-}
-.katex .sizing.reset-size5.size10,
-.katex .fontsize-ensurer.reset-size5.size10 {
- font-size: 2.49em;
-}
-.katex .sizing.reset-size6.size1,
-.katex .fontsize-ensurer.reset-size6.size1 {
- font-size: 0.41666667em;
-}
-.katex .sizing.reset-size6.size2,
-.katex .fontsize-ensurer.reset-size6.size2 {
- font-size: 0.58333333em;
-}
-.katex .sizing.reset-size6.size3,
-.katex .fontsize-ensurer.reset-size6.size3 {
- font-size: 0.66666667em;
-}
-.katex .sizing.reset-size6.size4,
-.katex .fontsize-ensurer.reset-size6.size4 {
- font-size: 0.75em;
-}
-.katex .sizing.reset-size6.size5,
-.katex .fontsize-ensurer.reset-size6.size5 {
- font-size: 0.83333333em;
-}
-.katex .sizing.reset-size6.size6,
-.katex .fontsize-ensurer.reset-size6.size6 {
- font-size: 1em;
-}
-.katex .sizing.reset-size6.size7,
-.katex .fontsize-ensurer.reset-size6.size7 {
- font-size: 1.2em;
-}
-.katex .sizing.reset-size6.size8,
-.katex .fontsize-ensurer.reset-size6.size8 {
- font-size: 1.44166667em;
-}
-.katex .sizing.reset-size6.size9,
-.katex .fontsize-ensurer.reset-size6.size9 {
- font-size: 1.725em;
-}
-.katex .sizing.reset-size6.size10,
-.katex .fontsize-ensurer.reset-size6.size10 {
- font-size: 2.075em;
-}
-.katex .sizing.reset-size7.size1,
-.katex .fontsize-ensurer.reset-size7.size1 {
- font-size: 0.34722222em;
-}
-.katex .sizing.reset-size7.size2,
-.katex .fontsize-ensurer.reset-size7.size2 {
- font-size: 0.48611111em;
-}
-.katex .sizing.reset-size7.size3,
-.katex .fontsize-ensurer.reset-size7.size3 {
- font-size: 0.55555556em;
-}
-.katex .sizing.reset-size7.size4,
-.katex .fontsize-ensurer.reset-size7.size4 {
- font-size: 0.625em;
-}
-.katex .sizing.reset-size7.size5,
-.katex .fontsize-ensurer.reset-size7.size5 {
- font-size: 0.69444444em;
-}
-.katex .sizing.reset-size7.size6,
-.katex .fontsize-ensurer.reset-size7.size6 {
- font-size: 0.83333333em;
-}
-.katex .sizing.reset-size7.size7,
-.katex .fontsize-ensurer.reset-size7.size7 {
- font-size: 1em;
-}
-.katex .sizing.reset-size7.size8,
-.katex .fontsize-ensurer.reset-size7.size8 {
- font-size: 1.20138889em;
-}
-.katex .sizing.reset-size7.size9,
-.katex .fontsize-ensurer.reset-size7.size9 {
- font-size: 1.4375em;
-}
-.katex .sizing.reset-size7.size10,
-.katex .fontsize-ensurer.reset-size7.size10 {
- font-size: 1.72916667em;
-}
-.katex .sizing.reset-size8.size1,
-.katex .fontsize-ensurer.reset-size8.size1 {
- font-size: 0.28901734em;
-}
-.katex .sizing.reset-size8.size2,
-.katex .fontsize-ensurer.reset-size8.size2 {
- font-size: 0.40462428em;
-}
-.katex .sizing.reset-size8.size3,
-.katex .fontsize-ensurer.reset-size8.size3 {
- font-size: 0.46242775em;
-}
-.katex .sizing.reset-size8.size4,
-.katex .fontsize-ensurer.reset-size8.size4 {
- font-size: 0.52023121em;
-}
-.katex .sizing.reset-size8.size5,
-.katex .fontsize-ensurer.reset-size8.size5 {
- font-size: 0.57803468em;
-}
-.katex .sizing.reset-size8.size6,
-.katex .fontsize-ensurer.reset-size8.size6 {
- font-size: 0.69364162em;
-}
-.katex .sizing.reset-size8.size7,
-.katex .fontsize-ensurer.reset-size8.size7 {
- font-size: 0.83236994em;
-}
-.katex .sizing.reset-size8.size8,
-.katex .fontsize-ensurer.reset-size8.size8 {
- font-size: 1em;
-}
-.katex .sizing.reset-size8.size9,
-.katex .fontsize-ensurer.reset-size8.size9 {
- font-size: 1.19653179em;
-}
-.katex .sizing.reset-size8.size10,
-.katex .fontsize-ensurer.reset-size8.size10 {
- font-size: 1.43930636em;
-}
-.katex .sizing.reset-size9.size1,
-.katex .fontsize-ensurer.reset-size9.size1 {
- font-size: 0.24154589em;
-}
-.katex .sizing.reset-size9.size2,
-.katex .fontsize-ensurer.reset-size9.size2 {
- font-size: 0.33816425em;
-}
-.katex .sizing.reset-size9.size3,
-.katex .fontsize-ensurer.reset-size9.size3 {
- font-size: 0.38647343em;
-}
-.katex .sizing.reset-size9.size4,
-.katex .fontsize-ensurer.reset-size9.size4 {
- font-size: 0.43478261em;
-}
-.katex .sizing.reset-size9.size5,
-.katex .fontsize-ensurer.reset-size9.size5 {
- font-size: 0.48309179em;
-}
-.katex .sizing.reset-size9.size6,
-.katex .fontsize-ensurer.reset-size9.size6 {
- font-size: 0.57971014em;
-}
-.katex .sizing.reset-size9.size7,
-.katex .fontsize-ensurer.reset-size9.size7 {
- font-size: 0.69565217em;
-}
-.katex .sizing.reset-size9.size8,
-.katex .fontsize-ensurer.reset-size9.size8 {
- font-size: 0.83574879em;
-}
-.katex .sizing.reset-size9.size9,
-.katex .fontsize-ensurer.reset-size9.size9 {
- font-size: 1em;
-}
-.katex .sizing.reset-size9.size10,
-.katex .fontsize-ensurer.reset-size9.size10 {
- font-size: 1.20289855em;
-}
-.katex .sizing.reset-size10.size1,
-.katex .fontsize-ensurer.reset-size10.size1 {
- font-size: 0.20080321em;
-}
-.katex .sizing.reset-size10.size2,
-.katex .fontsize-ensurer.reset-size10.size2 {
- font-size: 0.2811245em;
-}
-.katex .sizing.reset-size10.size3,
-.katex .fontsize-ensurer.reset-size10.size3 {
- font-size: 0.32128514em;
-}
-.katex .sizing.reset-size10.size4,
-.katex .fontsize-ensurer.reset-size10.size4 {
- font-size: 0.36144578em;
-}
-.katex .sizing.reset-size10.size5,
-.katex .fontsize-ensurer.reset-size10.size5 {
- font-size: 0.40160643em;
-}
-.katex .sizing.reset-size10.size6,
-.katex .fontsize-ensurer.reset-size10.size6 {
- font-size: 0.48192771em;
-}
-.katex .sizing.reset-size10.size7,
-.katex .fontsize-ensurer.reset-size10.size7 {
- font-size: 0.57831325em;
-}
-.katex .sizing.reset-size10.size8,
-.katex .fontsize-ensurer.reset-size10.size8 {
- font-size: 0.69477912em;
-}
-.katex .sizing.reset-size10.size9,
-.katex .fontsize-ensurer.reset-size10.size9 {
- font-size: 0.8313253em;
-}
-.katex .sizing.reset-size10.size10,
-.katex .fontsize-ensurer.reset-size10.size10 {
- font-size: 1em;
-}
-.katex .delimsizing.size1 {
- font-family: KaTeX_Size1;
-}
-.katex .delimsizing.size2 {
- font-family: KaTeX_Size2;
-}
-.katex .delimsizing.size3 {
- font-family: KaTeX_Size3;
-}
-.katex .delimsizing.size4 {
- font-family: KaTeX_Size4;
-}
-.katex .delimsizing.mult .delim-size1 > span {
- font-family: KaTeX_Size1;
-}
-.katex .delimsizing.mult .delim-size4 > span {
- font-family: KaTeX_Size4;
-}
-.katex .nulldelimiter {
- display: inline-block;
- width: 0.12em;
-}
-.katex .op-symbol {
- position: relative;
-}
-.katex .op-symbol.small-op {
- font-family: KaTeX_Size1;
-}
-.katex .op-symbol.large-op {
- font-family: KaTeX_Size2;
-}
-.katex .op-limits > .vlist > span {
- text-align: center;
-}
-.katex .accent > .vlist > span {
- text-align: center;
-}
-.katex .accent .accent-body > span {
- width: 0;
-}
-.katex .accent .accent-body.accent-vec > span {
- position: relative;
- left: 0.326em;
-}
-.katex .mtable .vertical-separator {
- display: inline-block;
- margin: 0 -0.025em;
- border-right: 0.05em solid black;
-}
-.katex .mtable .arraycolsep {
- display: inline-block;
-}
-.katex .mtable .col-align-c > .vlist {
- text-align: center;
-}
-.katex .mtable .col-align-l > .vlist {
- text-align: left;
-}
-.katex .mtable .col-align-r > .vlist {
- text-align: right;
-}
diff --git a/vendor/gitignore/Android.gitignore b/vendor/gitignore/Android.gitignore
index addf405e4f5..d57137223ed 100644
--- a/vendor/gitignore/Android.gitignore
+++ b/vendor/gitignore/Android.gitignore
@@ -54,3 +54,10 @@ google-services.json
freeline.py
freeline/
freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
diff --git a/vendor/gitignore/Dart.gitignore b/vendor/gitignore/Dart.gitignore
index 4d2a4d6db7c..58950beb4fa 100644
--- a/vendor/gitignore/Dart.gitignore
+++ b/vendor/gitignore/Dart.gitignore
@@ -1,6 +1,7 @@
# See https://www.dartlang.org/tools/private-files.html
# Files and directories created by pub
+.dart_tool/
.packages
.pub/
build/
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index a30eacf1d98..9c01e12b050 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -1,4 +1,4 @@
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
@@ -9,7 +9,6 @@
# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
-.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
diff --git a/vendor/gitignore/Python.gitignore b/vendor/gitignore/Python.gitignore
index af2f537516d..b989be6ca15 100644
--- a/vendor/gitignore/Python.gitignore
+++ b/vendor/gitignore/Python.gitignore
@@ -45,6 +45,7 @@ nosetests.xml
coverage.xml
*.cover
.hypothesis/
+.pytest_cache/
# Translations
*.mo
diff --git a/vendor/gitignore/ROS.gitignore b/vendor/gitignore/ROS.gitignore
index 425641f2c3a..35d74bb771f 100644
--- a/vendor/gitignore/ROS.gitignore
+++ b/vendor/gitignore/ROS.gitignore
@@ -13,6 +13,8 @@ msg/*Feedback.msg
msg/*Goal.msg
msg/*Result.msg
msg/_*.py
+build_isolated/
+devel_isolated/
# Generated by dynamic reconfigure
*.cfgc
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index 9bb63365618..5359e544bcf 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -10,6 +10,7 @@
*.fot
*.cb
*.cb2
+.*.lb
## Intermediate documents:
*.dvi
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index d3d5371b415..c49041ff7d2 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -237,6 +237,7 @@ _UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
+ServiceFabricBackup/
# SQL Server files
*.mdf
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index b2439d96e00..0ac8885405e 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -110,12 +110,15 @@ performance:
kubernetes: active
sast:
- image: registry.gitlab.com/gitlab-org/gl-sast:latest
+ image: docker:latest
variables:
- POSTGRES_DB: "false"
+ DOCKER_DRIVER: overlay2
allow_failure: true
+ services:
+ - docker:dind
script:
- - sast .
+ - setup_docker
+ - sast
artifacts:
paths: [gl-sast-report.json]
@@ -285,6 +288,12 @@ production:
export TILLER_NAMESPACE=$KUBE_NAMESPACE
function sast_container() {
+ if [[ -n "$CI_REGISTRY_USER" ]]; then
+ echo "Logging to GitLab Container Registry with CI credentials..."
+ docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" "$CI_REGISTRY"
+ echo ""
+ fi
+
docker run -d --name db arminc/clair-db:latest
docker run -p 6060:6060 --link db:postgres -d --name clair arminc/clair-local-scan:v2.0.1
apk add -U wget ca-certificates
@@ -309,7 +318,20 @@ production:
function sast() {
case "$CI_SERVER_VERSION" in
*-ee)
- /app/bin/run "$@"
+ # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable"
+ SAST_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+
+ # Deprecation notice for CONFIDENCE_LEVEL variable
+ if [ -z "$SAST_CONFIDENCE_LEVEL" -a "$CONFIDENCE_LEVEL" ]; then
+ SAST_CONFIDENCE_LEVEL="$CONFIDENCE_LEVEL"
+ echo "WARNING: CONFIDENCE_LEVEL is deprecated and MUST be replaced with SAST_CONFIDENCE_LEVEL"
+ fi
+
+ docker run --env SAST_CONFIDENCE_LEVEL="${SAST_CONFIDENCE_LEVEL:-3}" \
+ --env SAST_DISABLE_REMOTE_CHECKS="${SAST_DISABLE_REMOTE_CHECKS:-false}" \
+ --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
;;
*)
echo "GitLab EE is required"
@@ -346,6 +368,12 @@ production:
replicas="$new_replicas"
fi
+ if [[ "$CI_PROJECT_VISIBILITY" != "public" ]]; then
+ secret_name='gitlab-registry'
+ else
+ secret_name=''
+ fi
+
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
@@ -353,6 +381,7 @@ production:
--set image.repository="$CI_APPLICATION_REPOSITORY" \
--set image.tag="$CI_APPLICATION_TAG" \
--set image.pullPolicy=IfNotPresent \
+ --set image.secrets[0].name="$secret_name" \
--set application.track="$track" \
--set application.database_url="$DATABASE_URL" \
--set service.url="$CI_ENVIRONMENT_URL" \
@@ -482,6 +511,9 @@ production:
function create_secret() {
echo "Create secret..."
+ if [[ "$CI_PROJECT_VISIBILITY" == "public" ]]; then
+ return
+ fi
kubectl create secret -n "$KUBE_NAMESPACE" \
docker-registry gitlab-registry \
diff --git a/vendor/ingress/values.yaml b/vendor/ingress/values.yaml
index cdb7da77e86..a6b499953bf 100644
--- a/vendor/ingress/values.yaml
+++ b/vendor/ingress/values.yaml
@@ -2,7 +2,8 @@ controller:
image:
tag: "0.10.2"
repository: "quay.io/kubernetes-ingress-controller/nginx-ingress-controller"
- stats.enabled: true
+ stats:
+ enabled: true
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "10254"
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index e3ccf080f74..8f127468e25 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -1,5 +1,22 @@
+@babel/code-frame,7.0.0-beta.32,MIT
+@babel/helper-function-name,7.0.0-beta.32,MIT
+@babel/helper-get-function-arity,7.0.0-beta.32,MIT
+@babel/template,7.0.0-beta.32,MIT
+@babel/traverse,7.0.0-beta.32,MIT
+@babel/types,7.0.0-beta.32,MIT
+@gitlab-org/gitlab-svgs,1.8.0,SEE LICENSE IN LICENSE
+@types/jquery,2.0.48,MIT
+JSONStream,1.3.2,MIT
RedCloth,4.3.2,MIT
+abbrev,1.0.9,ISC
+accepts,1.3.3,MIT
ace-rails-ap,4.1.2,MIT
+acorn,3.3.0,MIT
+acorn,4.0.13,MIT
+acorn,5.1.1,MIT
+acorn,5.3.0,MIT
+acorn-dynamic-import,2.0.2,MIT
+acorn-jsx,3.0.1,MIT
actionmailer,4.2.10,MIT
actionpack,4.2.10,MIT
actionview,4.2.10,MIT
@@ -9,70 +26,518 @@ activerecord,4.2.10,MIT
activesupport,4.2.10,MIT
acts-as-taggable-on,4.0.0,MIT
addressable,2.5.2,Apache 2.0
+addressparser,1.0.1,MIT
+after,0.8.2,MIT
+agent-base,2.1.1,MIT
+ajv,4.11.8,MIT
+ajv,5.2.2,MIT
+ajv,5.5.2,MIT
+ajv-keywords,1.5.1,MIT
+ajv-keywords,2.1.0,MIT
akismet,2.0.0,MIT
+align-text,0.1.4,MIT
allocations,1.0.5,MIT
+alphanum-sort,1.0.2,MIT
+amdefine,1.0.1,BSD-3-Clause OR MIT
+ansi-escapes,1.4.0,MIT
+ansi-html,0.0.5,"Apache, Version 2.0"
+ansi-html,0.0.7,Apache 2.0
+ansi-regex,2.1.1,MIT
+ansi-regex,3.0.0,MIT
+ansi-styles,2.2.1,MIT
+ansi-styles,3.2.0,MIT
+anymatch,1.3.2,ISC
+append-transform,0.4.0,MIT
+aproba,1.1.2,ISC
+are-we-there-yet,1.1.4,ISC
arel,6.0.4,MIT
+argparse,1.0.9,MIT
+arr-diff,2.0.0,MIT
+arr-flatten,1.0.1,MIT
+array-filter,0.0.1,MIT
+array-find,1.0.0,MIT
+array-find-index,1.0.2,MIT
+array-flatten,1.1.1,MIT
+array-flatten,2.1.1,MIT
+array-map,0.0.0,MIT
+array-reduce,0.0.0,MIT
+array-slice,0.2.3,MIT
+array-union,1.0.2,MIT
+array-uniq,1.0.3,MIT
+array-unique,0.2.1,MIT
+arraybuffer.slice,0.0.7,MIT
+arrify,1.0.1,MIT
asana,0.6.0,MIT
asciidoctor,1.5.3,MIT
asciidoctor-plantuml,0.0.7,MIT
+asn1,0.2.3,MIT
+asn1.js,4.9.1,MIT
+assert,1.4.1,MIT
+assert-plus,0.2.0,MIT
+assert-plus,1.0.0,MIT
asset_sync,2.2.0,MIT
+ast-types,0.10.1,MIT
+astw,2.2.0,MIT
+async,0.9.2,MIT
+async,1.5.2,MIT
+async,2.1.5,MIT
+async,2.4.1,MIT
+async-each,1.0.1,MIT
+async-limiter,1.0.0,MIT
+asynckit,0.4.0,MIT
atomic,1.1.99,Apache 2.0
attr_encrypted,3.0.3,MIT
attr_required,1.0.0,MIT
+autoprefixer,6.7.7,MIT
autoprefixer-rails,6.2.3,MIT
+autosize,4.0.0,MIT
+aws-sign2,0.6.0,Apache 2.0
+aws-sign2,0.7.0,Apache 2.0
+aws4,1.6.0,MIT
axiom-types,0.1.1,MIT
+axios,0.15.3,MIT
+axios,0.17.1,MIT
+axios-mock-adapter,1.10.0,MIT
+babel-code-frame,6.26.0,MIT
+babel-core,6.26.0,MIT
+babel-eslint,8.0.2,MIT
+babel-generator,6.26.0,MIT
+babel-helper-bindify-decorators,6.24.1,MIT
+babel-helper-builder-binary-assignment-operator-visitor,6.24.1,MIT
+babel-helper-call-delegate,6.24.1,MIT
+babel-helper-define-map,6.26.0,MIT
+babel-helper-explode-assignable-expression,6.24.1,MIT
+babel-helper-explode-class,6.24.1,MIT
+babel-helper-function-name,6.24.1,MIT
+babel-helper-get-function-arity,6.24.1,MIT
+babel-helper-hoist-variables,6.24.1,MIT
+babel-helper-optimise-call-expression,6.24.1,MIT
+babel-helper-regex,6.26.0,MIT
+babel-helper-remap-async-to-generator,6.24.1,MIT
+babel-helper-replace-supers,6.24.1,MIT
+babel-helpers,6.24.1,MIT
+babel-loader,7.1.2,MIT
+babel-messages,6.23.0,MIT
+babel-plugin-check-es2015-constants,6.22.0,MIT
+babel-plugin-istanbul,4.1.5,New BSD
+babel-plugin-syntax-async-functions,6.13.0,MIT
+babel-plugin-syntax-async-generators,6.13.0,MIT
+babel-plugin-syntax-class-properties,6.13.0,MIT
+babel-plugin-syntax-decorators,6.13.0,MIT
+babel-plugin-syntax-dynamic-import,6.18.0,MIT
+babel-plugin-syntax-exponentiation-operator,6.13.0,MIT
+babel-plugin-syntax-object-rest-spread,6.13.0,MIT
+babel-plugin-syntax-trailing-function-commas,6.22.0,MIT
+babel-plugin-transform-async-generator-functions,6.24.1,MIT
+babel-plugin-transform-async-to-generator,6.24.1,MIT
+babel-plugin-transform-class-properties,6.24.1,MIT
+babel-plugin-transform-decorators,6.24.1,MIT
+babel-plugin-transform-define,1.3.0,MIT
+babel-plugin-transform-es2015-arrow-functions,6.22.0,MIT
+babel-plugin-transform-es2015-block-scoped-functions,6.22.0,MIT
+babel-plugin-transform-es2015-block-scoping,6.26.0,MIT
+babel-plugin-transform-es2015-classes,6.24.1,MIT
+babel-plugin-transform-es2015-computed-properties,6.24.1,MIT
+babel-plugin-transform-es2015-destructuring,6.23.0,MIT
+babel-plugin-transform-es2015-duplicate-keys,6.24.1,MIT
+babel-plugin-transform-es2015-for-of,6.23.0,MIT
+babel-plugin-transform-es2015-function-name,6.24.1,MIT
+babel-plugin-transform-es2015-literals,6.22.0,MIT
+babel-plugin-transform-es2015-modules-amd,6.24.1,MIT
+babel-plugin-transform-es2015-modules-commonjs,6.26.0,MIT
+babel-plugin-transform-es2015-modules-systemjs,6.24.1,MIT
+babel-plugin-transform-es2015-modules-umd,6.24.1,MIT
+babel-plugin-transform-es2015-object-super,6.24.1,MIT
+babel-plugin-transform-es2015-parameters,6.24.1,MIT
+babel-plugin-transform-es2015-shorthand-properties,6.24.1,MIT
+babel-plugin-transform-es2015-spread,6.22.0,MIT
+babel-plugin-transform-es2015-sticky-regex,6.24.1,MIT
+babel-plugin-transform-es2015-template-literals,6.22.0,MIT
+babel-plugin-transform-es2015-typeof-symbol,6.23.0,MIT
+babel-plugin-transform-es2015-unicode-regex,6.24.1,MIT
+babel-plugin-transform-exponentiation-operator,6.24.1,MIT
+babel-plugin-transform-object-rest-spread,6.23.0,MIT
+babel-plugin-transform-regenerator,6.26.0,MIT
+babel-plugin-transform-strict-mode,6.24.1,MIT
+babel-preset-es2015,6.24.1,MIT
+babel-preset-es2016,6.24.1,MIT
+babel-preset-es2017,6.24.1,MIT
+babel-preset-latest,6.24.1,MIT
+babel-preset-stage-2,6.24.1,MIT
+babel-preset-stage-3,6.24.1,MIT
+babel-register,6.26.0,MIT
+babel-runtime,6.26.0,MIT
+babel-template,6.26.0,MIT
+babel-traverse,6.26.0,MIT
+babel-types,6.26.0,MIT
babosa,1.0.2,MIT
+babylon,6.18.0,MIT
+babylon,7.0.0-beta.32,MIT
+backo2,1.0.2,MIT
+balanced-match,0.4.2,MIT
+balanced-match,1.0.0,MIT
base32,0.3.2,MIT
+base64-arraybuffer,0.1.5,MIT
+base64-js,1.2.0,MIT
+base64id,1.0.0,MIT
+batch,0.6.1,MIT
batch-loader,1.2.1,MIT
bcrypt,3.1.11,MIT
+bcrypt-pbkdf,1.0.1,New BSD
bcrypt_pbkdf,1.0.0,MIT
+better-assert,1.0.2,MIT
+big.js,3.1.3,MIT
+binary-extensions,1.10.0,MIT
bindata,2.4.1,ruby
+bl,1.1.2,MIT
+blackst0ne-mermaid,7.1.0-fixed,MIT
+blob,0.0.4,MIT*
+block-stream,0.0.9,ISC
+bluebird,2.11.0,MIT
+bluebird,3.5.0,MIT
+bn.js,4.11.6,MIT
+body-parser,1.17.2,MIT
+bonjour,3.5.0,MIT
+boom,2.10.1,New BSD
+boom,4.3.1,New BSD
+boom,5.2.0,New BSD
bootstrap-sass,3.3.6,MIT
bootstrap_form,2.7.0,MIT
+brace-expansion,1.1.8,MIT
+braces,0.1.5,MIT
+braces,1.8.5,MIT
+brorand,1.0.7,MIT
browser,2.2.0,MIT
+browser-pack,6.0.2,MIT
+browser-resolve,1.11.2,MIT
+browserify,14.5.0,MIT
+browserify-aes,1.0.6,MIT
+browserify-cipher,1.0.0,MIT
+browserify-des,1.0.0,MIT
+browserify-rsa,4.0.1,MIT
+browserify-sign,4.0.0,ISC
+browserify-zlib,0.1.4,MIT
+browserify-zlib,0.2.0,MIT
+browserslist,1.7.7,MIT
+buffer,4.9.1,MIT
+buffer,5.0.8,MIT
+buffer-indexof,1.1.0,MIT
+buffer-xor,1.0.3,MIT
builder,3.2.3,MIT
+buildmail,4.0.1,MIT
+builtin-modules,1.1.1,MIT
+builtin-status-codes,3.0.0,MIT
+bytes,2.4.0,MIT
+bytes,2.5.0,MIT
+bytes,3.0.0,MIT
+cached-path-relative,1.0.1,MIT
+caller-path,0.1.0,MIT
+callsite,1.0.0,MIT*
+callsites,0.2.0,MIT
+camelcase,1.2.1,MIT
+camelcase,2.1.1,MIT
+camelcase,3.0.0,MIT
+camelcase,4.1.0,MIT
+camelcase-keys,2.1.0,MIT
+caniuse-api,1.6.1,MIT
+caniuse-db,1.0.30000649,CC-BY-4.0
carrierwave,1.2.1,MIT
+caseless,0.11.0,Apache 2.0
+caseless,0.12.0,Apache 2.0
cause,0.1,MIT
+center-align,0.1.3,MIT
+chalk,1.1.3,MIT
+chalk,2.3.0,MIT
charlock_holmes,0.7.5,MIT
+chokidar,1.7.0,MIT
chronic,0.10.2,MIT
chronic_duration,0.10.6,MIT
chunky_png,1.3.5,MIT
+cipher-base,1.0.3,MIT
+circular-json,0.3.3,MIT
+circular-json,0.4.0,MIT
citrus,3.0.2,MIT
+clap,1.1.3,MIT
+classlist-polyfill,1.2.0,Unlicense
+cli-cursor,1.0.2,MIT
+cli-width,2.1.0,ISC
+clipboard,1.7.1,MIT
+cliui,2.1.0,ISC
+cliui,3.2.0,ISC
+clone,1.0.2,MIT
+co,3.0.6,MIT
+co,4.6.0,MIT
+coa,1.0.1,MIT
+code-point-at,1.1.0,MIT
coercible,1.0.0,MIT
+color,0.11.4,MIT
+color-convert,1.9.1,MIT
+color-name,1.1.2,MIT
+color-string,0.3.0,MIT
+colormin,1.1.2,MIT
+colors,1.1.2,MIT
+combine-lists,1.0.1,MIT
+combine-source-map,0.7.2,MIT
+combined-stream,1.0.5,MIT
+commander,2.9.0,MIT
+commondir,1.0.1,MIT
+component-bind,1.0.0,MIT*
+component-emitter,1.2.1,MIT
+component-inherit,0.0.3,MIT*
+compressible,2.0.11,MIT
+compression,1.7.0,MIT
+compression-webpack-plugin,1.0.0,MIT
+concat-map,0.0.1,MIT
+concat-stream,1.5.2,MIT
+concat-stream,1.6.0,MIT
concurrent-ruby-ext,1.0.5,MIT
+configstore,1.4.0,Simplified BSD
+connect,3.6.3,MIT
+connect-history-api-fallback,1.3.0,MIT
connection_pool,2.2.1,MIT
+console-browserify,1.1.0,MIT
+console-control-strings,1.1.0,ISC
+consolidate,0.14.5,MIT
+constants-browserify,1.0.0,MIT
+contains-path,0.1.0,MIT
+content-disposition,0.5.2,MIT
+content-type,1.0.2,MIT
+convert-source-map,1.1.3,MIT
+convert-source-map,1.5.0,MIT
+cookie,0.3.1,MIT
+cookie-signature,1.0.6,MIT
+copy-webpack-plugin,4.0.1,MIT
+core-js,2.3.0,MIT
+core-js,2.4.1,MIT
+core-js,2.5.1,MIT
+core-util-is,1.0.2,MIT
+cosmiconfig,2.1.1,MIT
crack,0.4.3,MIT
+create-ecdh,4.0.0,MIT
+create-hash,1.1.2,MIT
+create-hmac,1.1.4,MIT
creole,0.5.0,ruby
+cropper,2.3.0,MIT
+cross-spawn,5.1.0,MIT
+cryptiles,2.0.5,New BSD
+cryptiles,3.1.2,New BSD
+crypto-browserify,3.11.0,MIT
+crypto-browserify,3.12.0,MIT
+css-color-names,0.0.4,MIT
+css-loader,0.28.0,MIT
+css-selector-tokenizer,0.6.0,MIT
+css-selector-tokenizer,0.7.0,MIT
css_parser,1.5.0,MIT
+cssesc,0.1.0,MIT
+cssnano,3.10.0,MIT
+csso,2.3.2,MIT
+currently-unhandled,0.4.1,MIT
+custom-event,1.0.1,MIT
+d,0.1.1,MIT
+d,1.0.0,MIT
+d3,3.5.17,New BSD
+d3-array,1.2.1,New BSD
+d3-axis,1.0.8,New BSD
+d3-brush,1.0.4,New BSD
+d3-collection,1.0.4,New BSD
+d3-color,1.0.3,New BSD
+d3-dispatch,1.0.3,New BSD
+d3-drag,1.2.1,New BSD
+d3-ease,1.0.3,New BSD
+d3-format,1.2.1,New BSD
+d3-interpolate,1.1.6,New BSD
+d3-path,1.0.5,New BSD
+d3-scale,1.0.7,New BSD
+d3-selection,1.2.0,New BSD
+d3-shape,1.2.0,New BSD
+d3-time,1.0.8,New BSD
+d3-time-format,2.1.1,New BSD
+d3-timer,1.0.7,New BSD
+d3-transition,1.1.1,New BSD
d3_rails,3.5.11,MIT
+dagre-d3-renderer,0.4.24,MIT
+dagre-layout,0.8.0,MIT
+dashdash,1.14.1,MIT
+data-uri-to-buffer,1.2.0,MIT
+date-format,1.2.0,MIT
+date-now,0.1.4,MIT
+de-indent,1.0.2,MIT
+debug,2.2.0,MIT
+debug,2.6.7,MIT
+debug,2.6.8,MIT
+debug,2.6.9,MIT
+debug,3.1.0,MIT
debugger-ruby_core_source,1.3.8,MIT
+decamelize,1.2.0,MIT
deckar01-task_list,2.0.0,MIT
declarative,0.0.10,MIT
declarative-option,0.1.0,MIT
+decompress-response,3.3.0,MIT
+deep-equal,1.0.1,MIT
+deep-extend,0.4.2,MIT
+deep-is,0.1.3,MIT
+default-require-extensions,1.0.0,MIT
default_value_for,3.0.2,MIT
+define-properties,1.1.2,MIT
+defined,1.0.0,MIT
+degenerator,1.0.4,MIT
+del,2.2.2,MIT
+del,3.0.0,MIT
+delayed-stream,1.0.0,MIT
+delegate,3.1.2,MIT
+delegates,1.0.0,MIT
+depd,1.1.0,MIT
+depd,1.1.1,MIT
+deps-sort,2.0.0,MIT
+des.js,1.0.0,MIT
descendants_tracker,0.0.4,MIT
+destroy,1.0.4,MIT
+detect-indent,4.0.0,MIT
+detect-node,2.0.3,ISC
+detective,4.7.1,MIT
devise,4.2.0,MIT
devise-two-factor,3.0.0,MIT
+di,0.0.1,MIT
+diff,3.4.0,New BSD
diff-lcs,1.3,"MIT,Artistic-2.0,GPL-2.0+"
+diffie-hellman,5.0.2,MIT
diffy,3.1.0,MIT
+dns-equal,1.0.0,MIT
+dns-packet,1.2.2,MIT
+dns-txt,2.0.2,MIT
+doctrine,1.5.0,Simplified BSD
+doctrine,2.0.0,Apache 2.0
+document-register-element,1.3.0,MIT
+dom-serialize,2.2.1,MIT
+dom-serializer,0.1.0,MIT
+domain-browser,1.1.7,MIT
domain_name,0.5.20161021,"Simplified BSD,New BSD,Mozilla Public License 2.0"
+domelementtype,1.1.3,Simplified BSD
+domelementtype,1.3.0,Simplified BSD
+domhandler,2.4.1,Simplified BSD
+domutils,1.6.2,Simplified BSD
doorkeeper,4.2.6,MIT
doorkeeper-openid_connect,1.2.0,MIT
+double-ended-queue,2.1.0-0,MIT
+dropzone,4.2.0,MIT
dropzonejs-rails,0.7.2,MIT
+duplexer,0.1.1,MIT
+duplexer2,0.1.4,New BSD
+duplexer3,0.1.4,New BSD
+duplexify,3.5.1,MIT
+ecc-jsbn,0.1.1,MIT
+ee-first,1.1.1,MIT
+ejs,2.5.6,Apache 2.0
+electron-to-chromium,1.3.3,ISC
+elliptic,6.3.3,MIT
email_reply_trimmer,0.1.6,MIT
+emoji-unicode-version,0.2.1,MIT
+emojis-list,2.1.0,MIT
+encodeurl,1.0.1,MIT
encryptor,3.0.0,MIT
+end-of-stream,1.4.0,MIT
+engine.io,3.1.4,MIT
+engine.io-client,3.1.4,MIT
+engine.io-parser,2.1.2,MIT
+enhanced-resolve,0.9.1,MIT
+enhanced-resolve,3.4.1,MIT
+ent,2.2.0,MIT
+entities,1.1.1,Simplified BSD
equalizer,0.0.11,MIT
+errno,0.1.4,MIT
+error-ex,1.3.0,MIT
erubis,2.7.0,MIT
+es-abstract,1.8.2,MIT
+es-to-primitive,1.1.1,MIT
+es5-ext,0.10.24,MIT
+es6-iterator,2.0.1,MIT
+es6-map,0.1.5,MIT
+es6-promise,3.0.2,MIT
+es6-set,0.1.5,MIT
+es6-symbol,3.1.1,MIT
+es6-weak-map,2.0.1,MIT
+escape-html,1.0.3,MIT
+escape-string-regexp,1.0.5,MIT
escape_utils,1.1.1,MIT
+escodegen,1.8.1,Simplified BSD
+escodegen,1.9.0,Simplified BSD
+escope,3.6.0,Simplified BSD
+eslint,3.19.0,MIT
+eslint-config-airbnb-base,10.0.1,MIT
+eslint-import-resolver-node,0.2.3,MIT
+eslint-import-resolver-webpack,0.8.3,MIT
+eslint-module-utils,2.0.0,MIT
+eslint-plugin-filenames,1.1.0,MIT
+eslint-plugin-html,2.0.1,ISC
+eslint-plugin-import,2.2.0,MIT
+eslint-plugin-jasmine,2.2.0,MIT
+eslint-plugin-promise,3.5.0,ISC
+eslint-plugin-vue,4.0.1,MIT
+eslint-scope,3.7.1,Simplified BSD
+eslint-visitor-keys,1.0.0,Apache 2.0
+espree,3.5.0,Simplified BSD
+espree,3.5.2,Simplified BSD
+esprima,2.7.3,Simplified BSD
+esprima,3.1.3,Simplified BSD
+esprima,4.0.0,Simplified BSD
+esquery,1.0.0,New BSD
+esrecurse,4.1.0,Simplified BSD
+estraverse,1.9.3,Simplified BSD
+estraverse,4.1.1,Simplified BSD
+estraverse,4.2.0,Simplified BSD
+esutils,2.0.2,Simplified BSD
et-orbi,1.0.3,MIT
+etag,1.8.0,MIT
+eve-raphael,0.5.0,Apache 2.0
+event-emitter,0.3.5,MIT
+event-stream,3.3.4,MIT
+eventemitter3,1.2.0,MIT
+events,1.1.1,MIT
+eventsource,0.1.6,MIT
+evp_bytestokey,1.0.0,MIT
excon,0.57.1,MIT
+execa,0.7.0,MIT
execjs,2.6.0,MIT
+exit-hook,1.1.1,MIT
+expand-braces,0.1.2,MIT
+expand-brackets,0.1.5,MIT
+expand-range,0.1.1,MIT
+expand-range,1.8.2,MIT
+exports-loader,0.6.4,MIT
+express,4.15.4,MIT
expression_parser,0.9.0,MIT
+extend,3.0.1,MIT
+extglob,0.3.2,MIT
+extsprintf,1.3.0,MIT
faraday,0.12.2,MIT
faraday_middleware,0.11.0.1,MIT
faraday_middleware-multi_json,0.0.6,MIT
+fast-deep-equal,1.0.0,MIT
+fast-json-stable-stringify,2.0.0,MIT
+fast-levenshtein,2.0.6,MIT
+fast_blank,1.0.0,MIT
fast_gettext,1.4.0,"MIT,ruby"
+fastparse,1.1.1,MIT
+faye-websocket,0.10.0,MIT
+faye-websocket,0.11.1,MIT
+faye-websocket,0.7.3,MIT
ffi,1.9.18,New BSD
+figures,1.7.0,MIT
+file-entry-cache,2.0.0,MIT
+file-loader,0.11.1,MIT
+file-uri-to-path,1.0.0,MIT
+filename-regex,2.0.0,MIT
+fileset,2.0.3,MIT
+filesize,3.3.0,New BSD
+filesize,3.5.10,New BSD
+fill-range,2.2.3,MIT
+finalhandler,1.0.4,MIT
+find-cache-dir,1.0.0,MIT
+find-root,0.1.2,MIT
+find-up,1.1.2,MIT
+find-up,2.1.0,MIT
+flat-cache,1.2.2,MIT
+flatten,1.0.2,MIT
flipper,0.11.0,MIT
flipper-active_record,0.11.0,MIT
flipper-active_support_cache_store,0.11.0,MIT
@@ -86,93 +551,452 @@ fog-local,0.3.1,MIT
fog-openstack,0.1.21,MIT
fog-rackspace,0.1.1,MIT
fog-xml,0.1.3,MIT
+follow-redirects,1.0.0,MIT
+follow-redirects,1.2.6,MIT
font-awesome-rails,4.7.0.1,"MIT,SIL Open Font License"
+for-each,0.3.2,MIT
+for-in,0.1.6,MIT
+for-own,0.1.4,MIT
+foreach,2.0.5,MIT
+forever-agent,0.6.1,Apache 2.0
+form-data,2.0.0,MIT
+form-data,2.1.4,MIT
+form-data,2.3.1,MIT
formatador,0.2.5,MIT
+forwarded,0.1.0,MIT
+fresh,0.5.0,MIT
+from,0.1.7,MIT
+fs-access,1.0.1,MIT
+fs-extra,0.26.7,MIT
+fs.realpath,1.0.0,ISC
+fsevents,1.1.2,MIT
+fstream,1.0.11,ISC
+fstream-ignore,1.0.5,ISC
+ftp,0.3.10,MIT
+function-bind,1.1.0,MIT
+function-bind,1.1.1,MIT
+fuzzaldrin-plus,0.5.0,MIT
+gauge,2.7.4,ISC
gemnasium-gitlab-service,0.2.6,MIT
gemojione,3.3.0,MIT
+generate-function,2.0.0,MIT
+generate-object-property,1.2.0,MIT
+get-caller-file,1.0.2,ISC
+get-stdin,4.0.1,MIT
+get-stream,3.0.0,MIT
+get-uri,2.0.1,MIT
get_process_mem,0.2.0,MIT
+getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.2.0,MIT
-gitaly-proto,0.64.0,MIT
+gitaly-proto,0.84.0,MIT
github-linguist,4.7.6,MIT
github-markup,1.6.1,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
gitlab-grit,2.8.2,MIT
gitlab-markup,1.6.3,MIT
gitlab_omniauth-ldap,2.0.4,MIT
+glob,5.0.15,ISC
+glob,6.0.4,ISC
+glob,7.1.1,ISC
+glob,7.1.2,ISC
+glob-base,0.3.0,MIT
+glob-parent,2.0.0,ISC
globalid,0.4.1,MIT
+globals,10.4.0,MIT
+globals,9.18.0,MIT
+globby,5.0.0,MIT
+globby,6.1.0,MIT
gollum-grit_adapter,1.0.1,MIT
gollum-lib,4.2.7,MIT
gollum-rugged_adapter,0.4.4,MIT
gon,6.1.0,MIT
+good-listener,1.2.2,MIT
google-api-client,0.13.6,Apache 2.0
-google-protobuf,3.4.1.1,New BSD
+google-protobuf,3.5.1,New BSD
+googleapis-common-protos-types,1.0.1,Apache 2.0
googleauth,0.5.3,Apache 2.0
+got,3.3.1,MIT
+got,7.1.0,MIT
gpgme,2.0.13,LGPL-2.1+
+graceful-fs,4.1.11,ISC
+graceful-readlink,1.0.1,MIT
grape,1.0.0,MIT
grape-entity,0.6.0,MIT
grape-route-helpers,2.1.0,MIT
grape_logging,1.7.0,MIT
-grpc,1.4.5,New BSD
+graphlib,2.1.1,MIT
+grpc,1.8.3,Apache 2.0
+gzip-size,3.0.0,MIT
hamlit,2.6.1,MIT
+handle-thing,1.2.5,MIT
+handlebars,4.0.6,MIT
+har-schema,1.0.5,ISC
+har-schema,2.0.0,ISC
+har-validator,2.0.6,ISC
+har-validator,4.2.1,ISC
+har-validator,5.0.3,ISC
+has,1.0.1,MIT
+has-ansi,2.0.0,MIT
+has-binary2,1.0.2,MIT
+has-cors,1.1.0,MIT
+has-flag,1.0.0,MIT
+has-flag,2.0.0,MIT
+has-symbol-support-x,1.3.0,MIT
+has-to-string-tag-x,1.3.0,MIT
+has-unicode,2.0.1,ISC
+hash-sum,1.0.2,MIT
+hash.js,1.0.3,MIT
hashie,3.5.6,MIT
hashie-forbidden_attributes,0.1.1,MIT
+hawk,3.1.3,New BSD
+hawk,6.0.2,New BSD
+he,1.1.1,MIT
health_check,2.6.0,MIT
hipchat,1.5.2,MIT
+hipchat-notifier,1.1.0,MIT
+hoek,2.16.3,New BSD
+hoek,4.2.0,New BSD
+home-or-tmp,2.0.0,MIT
+hosted-git-info,2.2.0,ISC
+hpack.js,2.1.6,MIT
+html-comment-regex,1.1.1,MIT
+html-entities,1.2.0,MIT
html-pipeline,1.11.0,MIT
html2text,0.2.0,MIT
htmlentities,4.3.4,MIT
+htmlescape,1.1.1,MIT
+htmlparser2,3.9.2,MIT
http,0.9.8,MIT
http-cookie,1.0.3,MIT
+http-deceiver,1.2.7,MIT
+http-errors,1.6.1,MIT
+http-errors,1.6.2,MIT
http-form_data,1.0.1,MIT
+http-proxy,1.16.2,MIT
+http-proxy-agent,1.0.0,MIT
+http-proxy-middleware,0.17.4,MIT
+http-signature,1.1.1,MIT
+http-signature,1.2.0,MIT
http_parser.rb,0.6.0,MIT
httparty,0.13.7,MIT
httpclient,2.8.2,ruby
+httpntlm,1.6.1,MIT
+httpreq,0.4.24,MIT
+https-browserify,0.0.1,MIT
+https-browserify,1.0.0,MIT
+https-proxy-agent,1.0.0,MIT
i18n,0.9.1,MIT
ice_nine,0.11.2,MIT
+iconv-lite,0.4.15,MIT
+iconv-lite,0.4.19,MIT
+icss-replace-symbols,1.0.2,ISC
+ieee754,1.1.8,New BSD
+ignore,3.3.3,MIT
+ignore-by-default,1.0.1,ISC
+immediate,3.0.6,MIT
+imports-loader,0.7.1,MIT
+imurmurhash,0.1.4,MIT
+indent-string,2.1.0,MIT
+indexes-of,1.0.1,MIT
+indexof,0.0.1,MIT*
+infinity-agent,2.0.3,MIT
+inflection,1.10.0,MIT
+inflection,1.3.8,MIT
+inflight,1.0.6,ISC
influxdb,0.2.3,MIT
+inherits,2.0.1,ISC
+inherits,2.0.3,ISC
+ini,1.3.4,ISC
+inline-source-map,0.6.2,MIT
+inquirer,0.12.0,MIT
+insert-module-globals,7.0.1,MIT
+internal-ip,1.2.0,MIT
+interpret,1.0.1,MIT
+invariant,2.2.2,New BSD
+invert-kv,1.0.0,MIT
+ip,1.0.1,MIT
+ip,1.1.5,MIT
+ipaddr.js,1.4.0,MIT
ipaddress,0.8.3,MIT
+is-absolute,0.2.6,MIT
+is-absolute-url,2.1.0,MIT
+is-arrayish,0.2.1,MIT
+is-binary-path,1.0.1,MIT
+is-buffer,1.1.5,MIT
+is-buffer,1.1.6,MIT
+is-builtin-module,1.0.0,MIT
+is-callable,1.1.3,MIT
+is-date-object,1.0.1,MIT
+is-dotfile,1.0.2,MIT
+is-equal-shallow,0.1.3,MIT
+is-extendable,0.1.1,MIT
+is-extglob,1.0.0,MIT
+is-extglob,2.1.1,MIT
+is-finite,1.0.2,MIT
+is-fullwidth-code-point,1.0.0,MIT
+is-fullwidth-code-point,2.0.0,MIT
+is-function,1.0.1,MIT
+is-glob,2.0.1,MIT
+is-glob,3.1.0,MIT
+is-my-json-valid,2.16.0,MIT
+is-my-json-valid,2.17.1,MIT
+is-npm,1.0.0,MIT
+is-number,0.1.1,MIT
+is-number,2.1.0,MIT
+is-object,1.0.1,MIT
+is-path-cwd,1.0.0,MIT
+is-path-in-cwd,1.0.0,MIT
+is-path-inside,1.0.0,MIT
+is-plain-obj,1.1.0,MIT
+is-posix-bracket,0.1.1,MIT
+is-primitive,2.0.0,MIT
+is-property,1.0.2,MIT
+is-redirect,1.0.0,MIT
+is-regex,1.0.4,MIT
+is-relative,0.2.1,MIT
+is-resolvable,1.0.0,MIT
+is-retry-allowed,1.1.0,MIT
+is-stream,1.1.0,MIT
+is-svg,2.1.0,MIT
+is-symbol,1.0.1,MIT
+is-typedarray,1.0.0,MIT
+is-unc-path,0.1.2,MIT
+is-utf8,0.2.1,MIT
+is-windows,0.2.0,MIT
+isarray,0.0.1,MIT
+isarray,1.0.0,MIT
+isarray,2.0.1,MIT
+isbinaryfile,3.0.2,MIT
+isexe,1.1.2,ISC
+isobject,2.1.0,MIT
+isstream,0.1.2,MIT
+istanbul,0.4.5,New BSD
+istanbul-api,1.2.1,New BSD
+istanbul-lib-coverage,1.1.1,New BSD
+istanbul-lib-hook,1.1.0,New BSD
+istanbul-lib-instrument,1.9.1,New BSD
+istanbul-lib-report,1.1.2,New BSD
+istanbul-lib-source-maps,1.2.2,New BSD
+istanbul-reports,1.1.3,New BSD
+isurl,1.0.0,MIT
+jasmine-core,2.9.0,MIT
+jasmine-jquery,2.1.1,MIT
+jed,1.1.1,MIT
jira-ruby,1.4.1,MIT
+jquery,2.2.4,MIT
jquery-atwho-rails,1.3.2,MIT
jquery-rails,4.3.1,MIT
+jquery-ujs,1.2.2,MIT
+js-base64,2.1.9,New BSD
+js-cookie,2.1.3,MIT
+js-tokens,3.0.2,MIT
+js-yaml,3.7.0,MIT
+js-yaml,3.9.1,MIT
+jsbn,0.1.1,MIT
+jsesc,0.5.0,MIT
+jsesc,1.3.0,MIT
json,1.8.6,ruby
json-jwt,1.7.2,MIT
+json-loader,0.5.7,MIT
+json-schema,0.2.3,BSD
+json-schema-traverse,0.3.1,MIT
+json-stable-stringify,0.0.1,MIT
+json-stable-stringify,1.0.1,MIT
+json-stringify-safe,5.0.1,ISC
+json3,3.3.2,MIT
+json5,0.5.1,MIT
+jsonfile,2.4.0,MIT
+jsonify,0.0.0,Public Domain
+jsonparse,1.3.1,MIT
+jsonpointer,4.0.1,MIT
+jsprim,1.4.1,MIT
+jszip,3.1.3,(MIT OR GPL-3.0)
+jszip-utils,0.0.2,MIT or GPLv3
jwt,1.5.6,MIT
kaminari,1.0.1,MIT
kaminari-actionview,1.0.1,MIT
kaminari-activerecord,1.0.1,MIT
kaminari-core,1.0.1,MIT
+karma,2.0.0,MIT
+karma-chrome-launcher,2.2.0,MIT
+karma-coverage-istanbul-reporter,1.3.3,MIT
+karma-jasmine,1.1.1,MIT
+karma-mocha-reporter,2.2.5,MIT
+karma-sourcemap-loader,0.3.7,MIT
+karma-webpack,2.0.7,MIT
kgio,2.10.0,LGPL-2.1+
+kind-of,3.1.0,MIT
+klaw,1.3.1,MIT
kubeclient,2.2.0,MIT
+labeled-stream-splicer,2.0.0,MIT
+latest-version,1.0.1,MIT
+lazy-cache,1.0.4,MIT
+lcid,1.0.0,MIT
+levn,0.3.0,MIT
+lexical-scope,1.2.0,MIT
+libbase64,0.1.0,MIT
+libmime,3.0.0,MIT
+libqp,1.1.0,MIT
licensee,8.7.0,MIT
+lie,3.1.1,MIT
little-plugger,1.1.4,MIT
+load-json-file,1.1.0,MIT
+load-json-file,2.0.0,MIT
+loader-runner,2.3.0,MIT
+loader-utils,0.2.16,MIT
+loader-utils,1.1.0,MIT
locale,2.1.2,"ruby,LGPLv3+"
+locate-path,2.0.0,MIT
+lodash,3.10.1,MIT
+lodash,4.17.4,MIT
+lodash._baseassign,3.2.0,MIT
+lodash._basecopy,3.0.1,MIT
+lodash._baseget,3.7.2,MIT
+lodash._bindcallback,3.0.1,MIT
+lodash._createassigner,3.1.1,MIT
+lodash._getnative,3.9.1,MIT
+lodash._isiterateecall,3.0.9,MIT
+lodash._topath,3.8.1,MIT
+lodash.assign,3.2.0,MIT
+lodash.camelcase,4.1.1,MIT
+lodash.camelcase,4.3.0,MIT
+lodash.capitalize,4.2.1,MIT
+lodash.clonedeep,4.5.0,MIT
+lodash.cond,4.5.2,MIT
+lodash.deburr,4.1.0,MIT
+lodash.defaults,3.1.2,MIT
+lodash.escaperegexp,4.1.2,MIT
+lodash.get,3.7.0,MIT
+lodash.isarguments,3.1.0,MIT
+lodash.isarray,3.0.4,MIT
+lodash.kebabcase,4.0.1,MIT
+lodash.keys,3.1.2,MIT
+lodash.memoize,3.0.4,MIT
+lodash.memoize,4.1.2,MIT
+lodash.mergewith,4.6.0,MIT
+lodash.restparam,3.6.1,MIT
+lodash.snakecase,4.0.1,MIT
+lodash.uniq,4.5.0,MIT
+lodash.words,4.2.0,MIT
+log-symbols,2.1.0,MIT
+log4js,2.4.1,Apache 2.0
logging,2.2.2,MIT
+loggly,1.1.1,MIT
+loglevel,1.4.1,MIT
lograge,0.5.1,MIT
+longest,1.0.1,MIT
loofah,2.0.3,MIT
+loose-envify,1.3.1,MIT
+loud-rejection,1.6.0,MIT
+lowercase-keys,1.0.0,MIT
+lru-cache,2.2.4,MIT
+lru-cache,2.6.5,ISC
+lru-cache,4.1.1,ISC
+macaddress,0.2.8,MIT
mail,2.7.0,MIT
mail_room,0.9.1,MIT
+mailcomposer,4.0.1,MIT
+mailgun-js,0.7.15,MIT
+make-dir,1.0.0,MIT
+map-obj,1.0.1,MIT
+map-stream,0.1.0,Unknown
+marked,0.3.12,MIT
+math-expression-evaluator,1.2.16,MIT
+media-typer,0.3.0,MIT
+mem,1.1.0,MIT
memoist,0.16.0,MIT
+memory-fs,0.2.0,MIT
+memory-fs,0.4.1,MIT
+meow,3.7.0,MIT
+merge-descriptors,1.0.1,MIT
method_source,0.8.2,MIT
+methods,1.1.2,MIT
+micromatch,2.3.11,MIT
+miller-rabin,4.0.0,MIT
+mime,1.3.4,MIT
+mime,1.6.0,MIT
+mime-db,1.27.0,MIT
+mime-db,1.29.0,MIT
+mime-db,1.30.0,MIT
+mime-types,2.1.15,MIT
+mime-types,2.1.17,MIT
mime-types,3.1,MIT
mime-types-data,3.2016.0521,MIT
mimemagic,0.3.0,MIT
+mimic-fn,1.1.0,MIT
+mimic-response,1.0.0,MIT
mini_mime,0.1.4,MIT
mini_portile2,2.3.0,MIT
+minimalistic-assert,1.0.0,ISC
+minimatch,3.0.3,ISC
+minimatch,3.0.4,ISC
+minimist,0.0.8,MIT
+minimist,1.2.0,MIT
+mkdirp,0.5.1,MIT
+module-deps,4.1.1,MIT
+moment,2.19.2,MIT
+monaco-editor,0.10.0,MIT
+mousetrap,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
+ms,0.7.1,MIT
+ms,2.0.0,MIT
multi_json,1.12.2,MIT
multi_xml,0.6.0,MIT
+multicast-dns,6.1.1,MIT
+multicast-dns-service-types,1.1.0,MIT
multipart-post,2.0.0,MIT
mustermann,1.0.0,MIT
mustermann-grape,1.0.0,MIT
+mute-stream,0.0.5,ISC
mysql2,0.4.10,MIT
+name-all-modules-plugin,1.0.1,MIT
+nan,2.6.2,MIT
+natural-compare,1.4.0,MIT
+negotiator,0.6.1,MIT
+nested-error-stacks,1.0.2,MIT
net-ldap,0.16.0,MIT
net-ssh,4.1.0,MIT
+netmask,1.0.6,MIT
netrc,0.11.0,MIT
-nokogiri,1.8.1,MIT
+node-dir,0.1.17,MIT
+node-forge,0.6.33,New BSD
+node-libs-browser,1.1.1,MIT
+node-libs-browser,2.0.0,MIT
+node-pre-gyp,0.6.37,New BSD
+node-uuid,1.4.8,MIT
+nodemailer,2.7.2,MIT
+nodemailer-direct-transport,3.3.2,MIT
+nodemailer-fetch,1.6.0,MIT
+nodemailer-shared,1.1.0,MIT
+nodemailer-smtp-pool,2.8.2,MIT
+nodemailer-smtp-transport,2.7.2,MIT
+nodemailer-wellknown,0.1.10,MIT
+nodemon,1.11.0,MIT
+nokogiri,1.8.2,MIT
+nopt,1.0.10,MIT
+nopt,3.0.6,ISC
+nopt,4.0.1,ISC
+normalize-package-data,2.4.0,Simplified BSD
+normalize-path,2.1.1,MIT
+normalize-range,0.1.2,MIT
+normalize-url,1.9.1,MIT
+npm-run-path,2.0.2,MIT
+npmlog,4.1.2,ISC
+null-check,1.0.0,MIT
+num2fraction,1.2.2,MIT
+number-is-nan,1.0.1,MIT
numerizer,0.1.1,MIT
oauth,0.5.1,MIT
+oauth-sign,0.8.2,Apache 2.0
oauth2,1.4.0,MIT
+object-assign,3.0.0,MIT
+object-assign,4.1.1,MIT
+object-component,0.0.3,MIT*
+object-inspect,1.3.0,MIT
+object-keys,1.0.11,MIT
+object.omit,2.0.1,MIT
+obuf,1.1.1,MIT
octokit,4.6.2,MIT
oj,2.17.5,MIT
omniauth,1.4.2,MIT
@@ -193,10 +1017,58 @@ omniauth-saml,1.7.0,MIT
omniauth-shibboleth,1.2.1,MIT
omniauth-twitter,1.2.1,MIT
omniauth_crowd,2.2.3,MIT
+on-finished,2.3.0,MIT
+on-headers,1.0.1,MIT
+once,1.4.0,ISC
+onetime,1.1.0,MIT
+opener,1.4.3,(WTFPL OR MIT)
+opn,4.0.2,MIT
+optimist,0.6.1,MIT
+optionator,0.8.2,MIT
org-ruby,0.9.12,MIT
+original,1.0.0,MIT
orm_adapter,0.5.0,MIT
os,0.9.6,MIT
-paranoia,2.3.1,MIT
+os-browserify,0.2.1,MIT
+os-browserify,0.3.0,MIT
+os-homedir,1.0.2,MIT
+os-locale,1.4.0,MIT
+os-locale,2.1.0,MIT
+os-tmpdir,1.0.2,MIT
+osenv,0.1.4,ISC
+p-cancelable,0.3.0,MIT
+p-finally,1.0.0,MIT
+p-limit,1.1.0,MIT
+p-locate,2.0.0,MIT
+p-map,1.1.1,MIT
+p-timeout,1.2.0,MIT
+pac-proxy-agent,1.1.0,MIT
+pac-resolver,2.0.0,MIT
+package-json,1.2.0,MIT
+pako,0.2.9,MIT
+pako,1.0.5,(MIT AND Zlib)
+pako,1.0.6,(MIT AND Zlib)
+parents,1.0.1,MIT
+parse-asn1,5.0.0,ISC
+parse-glob,3.0.4,MIT
+parse-json,2.2.0,MIT
+parseqs,0.0.5,MIT
+parseuri,0.0.5,MIT
+parseurl,1.3.1,MIT
+path-browserify,0.0.0,MIT
+path-exists,2.1.0,MIT
+path-exists,3.0.0,MIT
+path-is-absolute,1.0.1,MIT
+path-is-inside,1.0.2,(WTFPL OR MIT)
+path-key,2.0.1,MIT
+path-parse,1.0.5,MIT
+path-platform,0.11.15,MIT
+path-proxy,1.0.0,MIT
+path-to-regexp,0.1.7,MIT
+path-type,1.1.0,MIT
+path-type,2.0.0,MIT
+pause-stream,0.0.11,Apache 2.0
+pbkdf2,3.0.9,MIT
peek,1.0.1,MIT
peek-gc,0.0.2,MIT
peek-host,1.0.0,MIT
@@ -206,18 +1078,98 @@ peek-pg,1.3.0,MIT
peek-rblineprof,0.2.0,MIT
peek-redis,1.2.0,MIT
peek-sidekiq,1.0.3,MIT
+performance-now,0.2.0,MIT
+performance-now,2.1.0,MIT
pg,0.18.4,"BSD,ruby,GPL"
+pify,2.3.0,MIT
+pify,3.0.0,MIT
+pikaday,1.6.1,MIT
+pinkie,2.0.4,MIT
+pinkie-promise,2.0.1,MIT
+pkg-dir,1.0.0,MIT
+pkg-dir,2.0.0,MIT
+pkg-up,1.0.0,MIT
+pluralize,1.2.1,MIT
po_to_json,1.0.1,MIT
+portfinder,1.0.13,MIT
posix-spawn,0.3.13,MIT
+postcss,5.2.16,MIT
+postcss,6.0.14,MIT
+postcss,6.0.15,MIT
+postcss-calc,5.3.1,MIT
+postcss-colormin,2.2.2,MIT
+postcss-convert-values,2.6.1,MIT
+postcss-discard-comments,2.0.4,MIT
+postcss-discard-duplicates,2.1.0,MIT
+postcss-discard-empty,2.1.0,MIT
+postcss-discard-overridden,0.1.1,MIT
+postcss-discard-unused,2.2.3,MIT
+postcss-filter-plugins,2.0.2,MIT
+postcss-load-config,1.2.0,MIT
+postcss-load-options,1.2.0,MIT
+postcss-load-plugins,2.3.0,MIT
+postcss-merge-idents,2.1.7,MIT
+postcss-merge-longhand,2.0.2,MIT
+postcss-merge-rules,2.1.2,MIT
+postcss-message-helpers,2.0.0,MIT
+postcss-minify-font-values,1.0.5,MIT
+postcss-minify-gradients,1.0.5,MIT
+postcss-minify-params,1.2.2,MIT
+postcss-minify-selectors,2.1.1,MIT
+postcss-modules-extract-imports,1.0.1,ISC
+postcss-modules-local-by-default,1.1.1,MIT
+postcss-modules-scope,1.0.2,ISC
+postcss-modules-values,1.2.2,ISC
+postcss-normalize-charset,1.1.1,MIT
+postcss-normalize-url,3.0.8,MIT
+postcss-ordered-values,2.2.3,MIT
+postcss-reduce-idents,2.4.0,MIT
+postcss-reduce-initial,1.0.1,MIT
+postcss-reduce-transforms,1.0.4,MIT
+postcss-selector-parser,2.2.3,MIT
+postcss-svgo,2.1.6,MIT
+postcss-unique-selectors,2.0.2,MIT
+postcss-value-parser,3.3.0,MIT
+postcss-zindex,2.2.0,MIT
+prelude-ls,1.1.2,MIT
premailer,1.10.4,New BSD
premailer-rails,1.9.7,MIT
-prometheus-client-mmap,0.7.0.beta44,Apache 2.0
+prepend-http,1.0.4,MIT
+preserve,0.2.0,MIT
+prettier,1.8.2,MIT
+prettier,1.9.2,MIT
+prismjs,1.6.0,MIT
+private,0.1.8,MIT
+process,0.11.9,MIT
+process-nextick-args,1.0.7,MIT
+progress,1.1.8,MIT
+prometheus-client-mmap,0.9.1,Apache 2.0
+proxy-addr,1.1.5,MIT
+proxy-agent,2.0.0,MIT
+prr,0.0.0,MIT
+ps-tree,1.1.0,MIT
+pseudomap,1.0.2,ISC
+public-encrypt,4.0.0,MIT
public_suffix,3.0.0,MIT
+punycode,1.3.2,MIT
+punycode,1.4.1,MIT
pyu-ruby-sasl,0.0.3.3,MIT
+q,1.4.1,MIT
+q,1.5.0,MIT
+qjobs,1.1.5,MIT
+qs,6.2.3,New BSD
+qs,6.4.0,New BSD
+qs,6.5.0,New BSD
+qs,6.5.1,New BSD
+query-string,4.3.2,MIT
+querystring,0.2.0,MIT
+querystring-es3,0.2.1,MIT
+querystringify,0.0.4,MIT
+querystringify,1.0.0,MIT
rack,1.6.8,MIT
rack-accept,0.4.5,MIT
rack-attack,4.4.1,MIT
-rack-cors,0.4.0,MIT
+rack-cors,1.0.2,MIT
rack-oauth2,1.2.3,MIT
rack-protection,1.5.3,MIT
rack-proxy,0.6.0,MIT
@@ -231,26 +1183,93 @@ railties,4.2.10,MIT
rainbow,2.2.2,MIT
raindrops,0.18.0,LGPL-2.1+
rake,12.3.0,MIT
+randomatic,1.1.6,MIT
+randombytes,2.0.3,MIT
+randombytes,2.0.6,MIT
+randomfill,1.0.3,MIT
+range-parser,1.2.0,MIT
+raphael,2.2.7,MIT
+raven-js,3.22.1,Simplified BSD
+raw-body,2.2.0,MIT
+raw-body,2.3.2,MIT
+raw-loader,0.5.1,MIT
+rb-fsevent,0.10.2,MIT
+rb-inotify,0.9.10,MIT
rbnacl,4.0.2,MIT
rbnacl-libsodium,1.0.11,MIT
+rc,1.2.1,(BSD-2-Clause OR MIT OR Apache-2.0)
rdoc,4.2.2,ruby
re2,1.1.1,New BSD
+react-dev-utils,0.5.2,New BSD
+read-all-stream,3.1.0,MIT
+read-only-stream,2.0.0,MIT
+read-pkg,1.1.0,MIT
+read-pkg,2.0.0,MIT
+read-pkg-up,1.0.1,MIT
+read-pkg-up,2.0.0,MIT
+readable-stream,1.1.14,MIT
+readable-stream,2.0.6,MIT
+readable-stream,2.3.3,MIT
+readdirp,2.1.0,MIT
+readline2,1.0.1,MIT
recaptcha,3.0.0,MIT
+rechoir,0.6.2,MIT
recursive-open-struct,1.0.0,MIT
+recursive-readdir,2.1.1,MIT
redcarpet,3.4.0,MIT
+redent,1.0.0,MIT
+redis,2.8.0,MIT
redis,3.3.5,MIT
redis-actionpack,5.0.2,MIT
redis-activesupport,5.0.4,MIT
+redis-commands,1.3.1,MIT
redis-namespace,1.5.2,MIT
+redis-parser,2.6.0,MIT
redis-rack,2.0.4,MIT
redis-rails,5.0.2,MIT
redis-store,1.4.1,MIT
+reduce-css-calc,1.3.0,MIT
+reduce-function-call,1.0.2,MIT
+regenerate,1.3.2,MIT
+regenerator-runtime,0.11.0,MIT
+regenerator-transform,0.10.1,BSD
+regex-cache,0.4.3,MIT
+regexpu-core,1.0.0,MIT
+regexpu-core,2.0.0,MIT
+registry-url,3.1.0,MIT
+regjsgen,0.2.0,MIT
+regjsparser,0.1.5,Simplified BSD
+remove-trailing-separator,1.1.0,ISC
+repeat-element,1.1.2,MIT
+repeat-string,0.2.2,MIT
+repeat-string,1.6.1,MIT
+repeating,1.1.3,MIT
+repeating,2.0.1,MIT
representable,3.0.4,MIT
+request,2.75.0,Apache 2.0
+request,2.81.0,Apache 2.0
+request,2.83.0,Apache 2.0
request_store,1.3.1,MIT
+requestretry,1.12.2,MIT
+require-all,2.2.0,MIT
+require-directory,2.1.1,MIT
+require-from-string,1.2.1,MIT
+require-main-filename,1.0.1,ISC
+require-uncached,1.0.3,MIT
+requires-port,1.0.0,MIT
+resolve,1.1.7,MIT
+resolve,1.4.0,MIT
+resolve,1.5.0,MIT
+resolve-from,1.0.1,MIT
responders,2.3.0,MIT
rest-client,2.0.0,MIT
+restore-cursor,1.0.1,MIT
+resumer,0.0.0,MIT
retriable,3.1.1,MIT
+right-align,0.1.3,MIT
+rimraf,2.6.1,ISC
rinku,2.0.0,ISC
+ripemd160,1.0.1,New BSD
rotp,2.1.2,MIT
rouge,2.2.1,MIT
rqrcode,0.7.0,MIT
@@ -263,51 +1282,282 @@ rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT
rugged,0.26.0,MIT
+run-async,0.1.0,MIT
+rx-lite,3.1.2,Apache 2.0
+safe-buffer,5.0.1,MIT
+safe-buffer,5.1.1,MIT
safe_yaml,1.0.4,MIT
sanitize,2.1.0,MIT
-sass,3.4.22,MIT
+sanitize-html,1.16.3,MIT
+sass,3.5.5,MIT
+sass-listen,4.0.0,MIT
sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
+sax,1.2.2,ISC
+schema-utils,0.3.0,MIT
securecompare,1.0.0,MIT
-seed-fu,2.3.6,MIT
+seed-fu,2.3.7,MIT
+select,1.1.2,MIT
+select-hose,2.0.0,MIT
+select2,3.5.2-browserify,Apache*
select2-rails,3.5.9.3,MIT
+selfsigned,1.10.1,MIT
+semver,5.0.3,ISC
+semver,5.3.0,ISC
+semver-diff,2.1.0,MIT
+send,0.15.4,MIT
sentry-raven,2.5.3,Apache 2.0
+serve-index,1.9.0,MIT
+serve-static,1.12.4,MIT
+set-blocking,2.0.0,ISC
+set-immediate-shim,1.0.1,MIT
+setimmediate,1.0.5,MIT
+setprototypeof,1.0.3,ISC
settingslogic,2.0.9,MIT
sexp_processor,4.9.0,MIT
+sha.js,2.4.8,MIT
+sha.js,2.4.9,MIT
+shasum,1.0.2,MIT
+shebang-command,1.2.0,MIT
+shebang-regex,1.0.0,MIT
+shell-quote,1.6.1,MIT
+shelljs,0.7.8,New BSD
sidekiq,5.0.5,LGPL
sidekiq-cron,0.6.0,MIT
sidekiq-limit_fetch,3.4.0,MIT
+signal-exit,3.0.2,ISC
signet,0.7.3,Apache 2.0
+slack-node,0.2.0,MIT
slack-notifier,1.5.1,MIT
+slash,1.0.0,MIT
+slice-ansi,0.0.4,MIT
+slide,1.1.6,ISC
+smart-buffer,1.1.15,MIT
+smtp-connection,2.12.0,MIT
+sntp,1.0.9,BSD
+sntp,2.1.0,BSD
+socket.io,2.0.4,MIT
+socket.io-adapter,1.1.1,MIT
+socket.io-client,2.0.4,MIT
+socket.io-parser,3.1.2,MIT
+sockjs,0.3.18,MIT
+sockjs-client,1.0.1,MIT
+sockjs-client,1.1.4,MIT
+socks,1.1.10,MIT
+socks,1.1.9,MIT
+socks-proxy-agent,2.1.1,MIT
+sort-keys,1.1.2,MIT
+source-list-map,0.1.8,MIT
+source-list-map,2.0.0,MIT
+source-map,0.2.0,New BSD
+source-map,0.4.4,New BSD
+source-map,0.5.6,New BSD
+source-map,0.5.7,New BSD
+source-map,0.6.1,New BSD
+source-map-support,0.4.18,MIT
+spdx-correct,1.0.2,Apache 2.0
+spdx-expression-parse,1.0.4,(MIT AND CC-BY-3.0)
+spdx-license-ids,1.2.2,Unlicense
+spdy,3.4.7,MIT
+spdy-transport,2.0.20,MIT
+split,0.3.3,MIT
+sprintf-js,1.0.3,New BSD
sprockets,3.7.1,MIT
sprockets-rails,3.2.1,MIT
+sql.js,0.4.0,MIT
+srcset,1.0.0,MIT
+sshpk,1.13.1,MIT
state_machines,0.4.0,MIT
state_machines-activemodel,0.4.0,MIT
state_machines-activerecord,0.4.0,MIT
+statuses,1.3.1,MIT
+stream-browserify,2.0.1,MIT
+stream-combiner,0.0.4,MIT
+stream-combiner2,1.1.1,MIT
+stream-http,2.6.3,MIT
+stream-http,2.7.2,MIT
+stream-shift,1.0.0,MIT
+stream-splicer,2.0.0,MIT
+streamroller,0.7.0,MIT
+strict-uri-encode,1.1.0,MIT
+string-length,1.0.1,MIT
+string-width,1.0.2,MIT
+string-width,2.0.0,MIT
+string.prototype.trim,1.1.2,MIT
+string_decoder,0.10.31,MIT
+string_decoder,1.0.3,MIT
stringex,2.7.1,MIT
+stringstream,0.0.5,MIT
+strip-ansi,3.0.1,MIT
+strip-ansi,4.0.0,MIT
+strip-bom,2.0.0,MIT
+strip-bom,3.0.0,MIT
+strip-eof,1.0.0,MIT
+strip-indent,1.0.1,MIT
+strip-json-comments,2.0.1,MIT
+subarg,1.0.0,MIT
+supports-color,2.0.0,MIT
+supports-color,3.2.3,MIT
+supports-color,4.2.1,MIT
+supports-color,4.5.0,MIT
+supports-color,5.1.0,MIT
+svg4everybody,2.1.9,CC0-1.0
+svgo,0.7.2,MIT
+syntax-error,1.3.0,MIT
sys-filesystem,1.1.6,Artistic 2.0
+table,3.8.3,New BSD
+tapable,0.1.10,MIT
+tapable,0.2.8,MIT
+tape,4.8.0,MIT
+tar,2.2.1,ISC
+tar-pack,3.4.0,Simplified BSD
temple,0.7.7,MIT
+test-exclude,4.1.1,ISC
text,1.3.1,MIT
+text-table,0.2.0,MIT
thor,0.19.4,MIT
thread_safe,0.3.6,Apache 2.0
+three,0.84.0,MIT
+three-orbit-controls,82.1.0,MIT
+three-stl-loader,1.0.4,MIT
+through,2.3.8,MIT
+through2,2.0.3,MIT
+thunkify,2.1.2,MIT
+thunky,0.1.0,MIT*
tilt,2.0.6,MIT
+time-stamp,2.0.0,MIT
+timeago.js,3.0.2,MIT
+timed-out,2.0.0,MIT
+timed-out,4.0.1,MIT
+timers-browserify,1.4.2,MIT
+timers-browserify,2.0.4,MIT
+timespan,2.3.0,MIT
timfel-krb5-auth,0.8.3,LGPL
+tiny-emitter,2.0.2,MIT
+tmp,0.0.31,MIT
+tmp,0.0.33,MIT
+to-array,0.1.4,MIT
+to-arraybuffer,1.0.1,MIT
+to-fast-properties,1.0.3,MIT
+to-fast-properties,2.0.0,MIT
toml-rb,0.3.15,MIT
+touch,1.0.0,ISC
+tough-cookie,2.3.2,New BSD
+tough-cookie,2.3.3,New BSD
+traverse,0.6.6,MIT
+trim-newlines,1.0.0,MIT
+trim-right,1.0.1,MIT
truncato,0.7.10,MIT
+tryit,1.0.3,MIT
+tsscmp,1.0.5,MIT
+tty-browserify,0.0.0,MIT
+tunnel-agent,0.4.3,Apache 2.0
+tunnel-agent,0.6.0,Apache 2.0
+tweetnacl,0.14.5,Unlicense
+type-check,0.3.2,MIT
+type-is,1.6.15,MIT
+typedarray,0.0.6,MIT
tzinfo,1.2.4,MIT
u2f,0.2.1,MIT
uber,0.1.0,MIT
uglifier,2.7.2,MIT
+uglify-js,2.8.29,Simplified BSD
+uglify-to-browserify,1.0.2,MIT
+uglifyjs-webpack-plugin,0.4.6,MIT
+uid-number,0.0.6,ISC
+ultron,1.1.0,MIT
+umd,3.0.1,MIT
+unc-path-regex,0.1.2,MIT
+undefsafe,0.0.3,MIT / http://rem.mit-license.org
+underscore,1.7.0,MIT
+underscore,1.8.3,MIT
unf,0.1.4,BSD
unf_ext,0.0.7.4,MIT
unicorn,5.1.0,ruby
unicorn-worker-killer,0.4.4,ruby
+uniq,1.0.1,MIT
+uniqid,4.1.1,MIT
+uniqs,2.0.0,MIT
+unpipe,1.0.0,MIT
+update-notifier,0.5.0,Simplified BSD
+url,0.11.0,MIT
+url-loader,0.5.8,MIT
+url-parse,1.0.5,MIT
+url-parse,1.1.7,MIT
+url-parse,1.1.9,MIT
+url-parse-lax,1.0.0,MIT
+url-to-options,1.0.1,MIT
url_safe_base64,0.2.2,MIT
+user-home,2.0.0,MIT
+useragent,2.2.1,MIT
+util,0.10.3,MIT
+util-deprecate,1.0.2,MIT
+utils-merge,1.0.0,MIT
+uuid,2.0.3,MIT
+uuid,3.1.0,MIT
+uws,0.14.5,Zlib
+validate-npm-package-license,3.0.1,Apache 2.0
validates_hostname,1.0.6,MIT
+vary,1.1.1,MIT
+vendors,1.0.1,MIT
+verror,1.10.0,MIT
version_sorter,2.1.0,MIT
virtus,1.0.5,MIT
+visibilityjs,1.2.4,MIT
+vm-browserify,0.0.4,MIT
vmstat,2.3.0,MIT
+void-elements,2.0.1,MIT
+vue,2.5.13,MIT
+vue-eslint-parser,2.0.1,MIT
+vue-hot-reload-api,2.2.4,MIT
+vue-loader,13.7.0,MIT
+vue-resource,1.3.5,MIT
+vue-router,3.0.1,MIT
+vue-style-loader,3.0.3,MIT
+vue-template-compiler,2.5.13,MIT
+vue-template-es2015-compiler,1.6.0,MIT
+vuex,3.0.1,MIT
warden,1.2.6,MIT
+watchpack,1.4.0,MIT
+wbuf,1.7.2,MIT
+webpack,3.5.5,MIT
+webpack-bundle-analyzer,2.8.2,MIT
+webpack-dev-middleware,1.11.0,MIT
+webpack-dev-middleware,1.12.2,MIT
+webpack-dev-server,2.7.1,MIT
webpack-rails,0.9.10,MIT
+webpack-sources,1.0.1,MIT
+webpack-stats-plugin,0.1.5,MIT
+websocket-driver,0.6.5,MIT
+websocket-extensions,0.1.1,MIT
+when,3.7.8,MIT
+whet.extend,0.9.9,MIT
+which,1.2.12,ISC
+which-module,1.0.0,ISC
+which-module,2.0.0,ISC
+wide-align,1.1.2,ISC
wikicloth,0.8.1,MIT
+window-size,0.1.0,MIT
+wordwrap,0.0.2,MIT
+wordwrap,0.0.3,MIT
+wordwrap,1.0.0,MIT
+worker-loader,1.1.0,MIT
+wrap-ansi,2.1.0,MIT
+wrappy,1.0.2,ISC
+write,0.2.1,MIT
+write-file-atomic,1.3.4,ISC
+ws,2.3.1,MIT
+ws,3.3.3,MIT
+xdg-basedir,2.0.0,MIT
xml-simple,1.1.5,ruby
+xmlhttprequest-ssl,1.5.5,MIT
+xregexp,2.0.0,MIT
+xtend,4.0.1,MIT
+y18n,3.2.1,ISC
+yallist,2.1.2,ISC
+yargs,3.10.0,MIT
+yargs,6.6.0,MIT
+yargs,8.0.2,MIT
+yargs-parser,4.2.1,ISC
+yargs-parser,7.0.0,ISC
+yeast,0.1.2,MIT
diff --git a/vendor/prometheus/values.yaml b/vendor/prometheus/values.yaml
index db967514be7..859f2ad82a4 100644
--- a/vendor/prometheus/values.yaml
+++ b/vendor/prometheus/values.yaml
@@ -10,6 +10,9 @@ nodeExporter:
pushgateway:
enabled: false
+rbac:
+ create: false
+
server:
image:
tag: v2.1.0
diff --git a/yarn.lock b/yarn.lock
index bc5c19464fb..4d7dc1be854 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -69,17 +69,28 @@ JSONStream@^1.0.3:
jsonparse "^1.2.0"
through ">=2.2.7 <3"
-abbrev@1, abbrev@1.0.x:
+abbrev@1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
+
+abbrev@1.0.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"
-accepts@1.3.3, accepts@~1.3.3:
+accepts@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.3.tgz#c3ca7434938648c3e0d9c1e328dd68b622c284ca"
dependencies:
mime-types "~2.1.11"
negotiator "0.6.1"
+accepts@~1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.4.tgz#86246758c7dd6d21a6474ff084a4740ec05eb21f"
+ dependencies:
+ mime-types "~2.1.16"
+ negotiator "0.6.1"
+
acorn-dynamic-import@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4"
@@ -92,6 +103,13 @@ acorn-jsx@^3.0.0:
dependencies:
acorn "^3.0.4"
+acorn-node@^1.2.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.3.0.tgz#5f86d73346743810ef1269b901dbcbded020861b"
+ dependencies:
+ acorn "^5.4.1"
+ xtend "^4.0.1"
+
acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
@@ -100,13 +118,17 @@ acorn@^4.0.3:
version "4.0.13"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787"
-acorn@^5.0.0, acorn@^5.0.3, acorn@^5.1.1:
+acorn@^5.0.0, acorn@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.1.1.tgz#53fe161111f912ab999ee887a90a0bc52822fd75"
-acorn@^5.2.1:
- version "5.3.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822"
+acorn@^5.2.1, acorn@^5.3.0, acorn@^5.4.1:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102"
+
+address@1.0.3, address@^1.0.1:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9"
addressparser@1.0.1:
version "1.0.1"
@@ -127,9 +149,9 @@ ajv-keywords@^1.0.0:
version "1.5.1"
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c"
-ajv-keywords@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.0.tgz#a296e17f7bfae7c1ce4f7e0de53d29cb32162df0"
+ajv-keywords@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be"
ajv@^4.7.0, ajv@^4.9.1:
version "4.11.8"
@@ -138,7 +160,7 @@ ajv@^4.7.0, ajv@^4.9.1:
co "^4.6.0"
json-stable-stringify "^1.0.1"
-ajv@^5.0.0, ajv@^5.1.5:
+ajv@^5.0.0:
version "5.2.2"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.2.2.tgz#47c68d69e86f5d953103b0074a9430dc63da5e39"
dependencies:
@@ -156,6 +178,14 @@ ajv@^5.1.0:
fast-json-stable-stringify "^2.0.0"
json-schema-traverse "^0.3.0"
+ajv@^6.1.0:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.1.1.tgz#978d597fbc2b7d0e5a5c3ddeb149a682f2abfa0e"
+ dependencies:
+ fast-deep-equal "^1.0.0"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.3.0"
+
align-text@^0.1.1, align-text@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117"
@@ -172,13 +202,29 @@ amdefine@>=0.0.4:
version "1.0.1"
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
+amqplib@^0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/amqplib/-/amqplib-0.5.2.tgz#d2d7313c7ffaa4d10bcf1e6252de4591b6cc7b63"
+ dependencies:
+ bitsyntax "~0.0.4"
+ bluebird "^3.4.6"
+ buffer-more-ints "0.0.2"
+ readable-stream "1.x >=1.1.9"
+ safe-buffer "^5.0.1"
+
+ansi-align@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
+ dependencies:
+ string-width "^2.0.0"
+
ansi-escapes@^1.1.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
-ansi-html@0.0.5:
- version "0.0.5"
- resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.5.tgz#0dcaa5a081206866bc240a3b773a184ea3b88b64"
+ansi-escapes@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
ansi-html@0.0.7:
version "0.0.7"
@@ -196,7 +242,7 @@ ansi-styles@^2.2.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
-ansi-styles@^3.1.0:
+ansi-styles@^3.1.0, ansi-styles@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.0.tgz#c159b8d5be0f9e5a6f346dab94f16ce022161b88"
dependencies:
@@ -209,15 +255,22 @@ anymatch@^1.3.0:
micromatch "^2.1.5"
normalize-path "^2.0.0"
+anymatch@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
+ dependencies:
+ micromatch "^3.1.4"
+ normalize-path "^2.1.1"
+
append-transform@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
dependencies:
default-require-extensions "^1.0.0"
-aproba@^1.0.3:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.2.tgz#45c6629094de4e96f693ef7eab74ae079c240fc1"
+aproba@^1.0.3, aproba@^1.1.1:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a"
are-we-there-yet@~1.1.2:
version "1.1.4"
@@ -238,9 +291,17 @@ arr-diff@^2.0.0:
dependencies:
arr-flatten "^1.0.1"
-arr-flatten@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b"
+arr-diff@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520"
+
+arr-flatten@^1.0.1, arr-flatten@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1"
+
+arr-union@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
array-filter@~0.0.0:
version "0.0.1"
@@ -262,6 +323,13 @@ array-flatten@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296"
+array-includes@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d"
+ dependencies:
+ define-properties "^1.1.2"
+ es-abstract "^1.7.0"
+
array-map@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662"
@@ -288,6 +356,10 @@ array-unique@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53"
+array-unique@^0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
+
arraybuffer.slice@~0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
@@ -297,8 +369,8 @@ arrify@^1.0.0, arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
asn1.js@^4.0.0:
- version "4.9.1"
- resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.9.1.tgz#48ba240b45a9280e94748990ba597d216617fd40"
+ version "4.10.1"
+ resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0"
dependencies:
bn.js "^4.0.0"
inherits "^2.0.1"
@@ -322,9 +394,13 @@ assert@^1.1.1, assert@^1.4.0:
dependencies:
util "0.10.3"
+assign-symbols@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
+
ast-types@0.x.x:
- version "0.10.1"
- resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd"
+ version "0.11.1"
+ resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.1.tgz#5bb3a8d5ba292c3f4ae94d46df37afc30300b990"
astw@^2.0.0:
version "2.2.0"
@@ -344,12 +420,18 @@ async@1.x, async@^1.4.0, async@^1.5.2:
version "1.5.2"
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
-async@2.4.1, async@^2.1.2, async@^2.1.4:
+async@^2.1.2, async@^2.1.4:
version "2.4.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7"
dependencies:
lodash "^4.14.0"
+async@^2.4.1:
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
+ dependencies:
+ lodash "^4.14.0"
+
async@~0.9.0:
version "0.9.2"
resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
@@ -364,6 +446,10 @@ asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+atob@^2.0.0:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d"
+
autoprefixer@^6.3.1:
version "6.7.7"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014"
@@ -410,7 +496,7 @@ axios@^0.17.1:
follow-redirects "^1.2.5"
is-buffer "^1.1.5"
-babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.26.0:
+babel-code-frame@6.26.0, babel-code-frame@^6.16.0, babel-code-frame@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
dependencies:
@@ -1026,13 +1112,25 @@ base64-arraybuffer@0.1.5:
resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.5.tgz#73926771923b5a19747ad666aa5cd4bf9c6e9ce8"
base64-js@^1.0.2:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.0.tgz#a39992d723584811982be5e290bb6a53d86700f1"
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.3.tgz#fb13668233d9614cf5fb4bce95a9ba4096cdf801"
base64id@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base64id/-/base64id-1.0.0.tgz#47688cb99bb6804f0e06d3e763b1c32e57d8e6b6"
+base@^0.11.1:
+ version "0.11.2"
+ resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f"
+ dependencies:
+ cache-base "^1.0.1"
+ class-utils "^0.3.5"
+ component-emitter "^1.2.1"
+ define-property "^1.0.0"
+ isobject "^3.0.1"
+ mixin-deep "^1.2.0"
+ pascalcase "^0.1.1"
+
batch@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16"
@@ -1049,13 +1147,27 @@ better-assert@~1.0.0:
dependencies:
callsite "1.0.0"
+bfj-node4@^5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/bfj-node4/-/bfj-node4-5.2.1.tgz#3a6aa2730cf6911ba2afb836c2f88f015d718f3f"
+ dependencies:
+ bluebird "^3.5.1"
+ check-types "^7.3.0"
+ tryer "^1.0.0"
+
big.js@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.1.3.tgz#4cada2193652eb3ca9ec8e55c9015669c9806978"
binary-extensions@^1.0.0:
- version "1.10.0"
- resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0"
+ version "1.11.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
+
+bitsyntax@~0.0.4:
+ version "0.0.4"
+ resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82"
+ dependencies:
+ buffer-more-ints "0.0.2"
bl@~1.1.2:
version "1.1.2"
@@ -1084,31 +1196,31 @@ block-stream@*:
dependencies:
inherits "~2.0.0"
-bluebird@^2.10.2:
- version "2.11.0"
- resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1"
-
-bluebird@^3.1.1, bluebird@^3.3.0:
+bluebird@^3.1.1:
version "3.5.0"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.0.tgz#791420d7f551eea2897453a8a77653f96606d67c"
+bluebird@^3.3.0, bluebird@^3.4.6, bluebird@^3.5.1:
+ version "3.5.1"
+ resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
+
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
- version "4.11.6"
- resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.6.tgz#53344adb14617a13f6e8dd2ce28905d1c0ba3215"
+ version "4.11.8"
+ resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
-body-parser@^1.16.1:
- version "1.17.2"
- resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.17.2.tgz#f8892abc8f9e627d42aedafbca66bf5ab99104ee"
+body-parser@1.18.2, body-parser@^1.16.1:
+ version "1.18.2"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454"
dependencies:
- bytes "2.4.0"
- content-type "~1.0.2"
- debug "2.6.7"
- depd "~1.1.0"
- http-errors "~1.6.1"
- iconv-lite "0.4.15"
+ bytes "3.0.0"
+ content-type "~1.0.4"
+ debug "2.6.9"
+ depd "~1.1.1"
+ http-errors "~1.6.2"
+ iconv-lite "0.4.19"
on-finished "~2.3.0"
- qs "6.4.0"
- raw-body "~2.2.0"
+ qs "6.5.1"
+ raw-body "2.3.2"
type-is "~1.6.15"
bonjour@^3.5.0:
@@ -1144,13 +1256,32 @@ bootstrap-sass@^3.3.6:
version "3.3.6"
resolved "https://registry.yarnpkg.com/bootstrap-sass/-/bootstrap-sass-3.3.6.tgz#363b0d300e868d3e70134c1a742bb17288444fd1"
-brace-expansion@^1.0.0, brace-expansion@^1.1.7, brace-expansion@^1.1.8:
+boxen@^1.2.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
+ dependencies:
+ ansi-align "^2.0.0"
+ camelcase "^4.0.0"
+ chalk "^2.0.1"
+ cli-boxes "^1.0.0"
+ string-width "^2.0.0"
+ term-size "^1.2.0"
+ widest-line "^2.0.0"
+
+brace-expansion@^1.0.0, brace-expansion@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.8.tgz#c07b211c7c952ec1f8efd51a77ef0d1d3990a292"
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
braces@^0.1.2:
version "0.1.5"
resolved "https://registry.yarnpkg.com/braces/-/braces-0.1.5.tgz#c085711085291d8b75fdd74eab0f8597280711e6"
@@ -1165,17 +1296,35 @@ braces@^1.8.2:
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"
+ dependencies:
+ arr-flatten "^1.1.0"
+ array-unique "^0.3.2"
+ define-property "^1.0.0"
+ extend-shallow "^2.0.1"
+ fill-range "^4.0.0"
+ isobject "^3.0.1"
+ kind-of "^6.0.2"
+ repeat-element "^1.1.2"
+ snapdragon "^0.8.1"
+ snapdragon-node "^2.0.1"
+ split-string "^3.0.2"
+ to-regex "^3.0.1"
+
brorand@^1.0.1:
- version "1.0.7"
- resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.0.7.tgz#6677fa5e4901bdbf9c9ec2a748e28dca407a9bfc"
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
browser-pack@^6.0.1:
- version "6.0.2"
- resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.2.tgz#f86cd6cef4f5300c8e63e07a4d512f65fbff4531"
+ version "6.0.4"
+ resolved "https://registry.yarnpkg.com/browser-pack/-/browser-pack-6.0.4.tgz#9a73beb3b48f9e36868be007b64400102c04a99f"
dependencies:
JSONStream "^1.0.3"
- combine-source-map "~0.7.1"
+ combine-source-map "~0.8.0"
defined "^1.0.0"
+ safe-buffer "^5.1.1"
through2 "^2.0.0"
umd "^3.0.0"
@@ -1186,14 +1335,15 @@ browser-resolve@^1.11.0, browser-resolve@^1.7.0:
resolve "1.1.7"
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
- version "1.0.6"
- resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.0.6.tgz#5e7725dbdef1fd5930d4ebab48567ce451c48a0a"
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f"
dependencies:
- buffer-xor "^1.0.2"
+ buffer-xor "^1.0.3"
cipher-base "^1.0.0"
create-hash "^1.1.0"
- evp_bytestokey "^1.0.0"
+ evp_bytestokey "^1.0.3"
inherits "^2.0.1"
+ safe-buffer "^5.0.1"
browserify-cipher@^1.0.0:
version "1.0.0"
@@ -1219,8 +1369,8 @@ browserify-rsa@^4.0.0:
randombytes "^2.0.1"
browserify-sign@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.0.tgz#10773910c3c206d5420a46aad8694f820b85968f"
+ version "4.0.4"
+ resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298"
dependencies:
bn.js "^4.1.1"
browserify-rsa "^4.0.0"
@@ -1305,7 +1455,11 @@ buffer-indexof@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.0.tgz#f54f647c4f4e25228baa656a2e57e43d5f270982"
-buffer-xor@^1.0.2:
+buffer-more-ints@0.0.2:
+ version "0.0.2"
+ resolved "https://registry.yarnpkg.com/buffer-more-ints/-/buffer-more-ints-0.0.2.tgz#26b3885d10fa13db7fc01aae3aab870199e0124c"
+
+buffer-xor@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9"
@@ -1318,8 +1472,8 @@ buffer@^4.3.0:
isarray "^1.0.0"
buffer@^5.0.2:
- version "5.0.8"
- resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.0.8.tgz#84daa52e7cf2fa8ce4195bc5cf0f7809e0930b24"
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.1.0.tgz#c913e43678c7cb7c8bd16afbcddb6c5505e8f9fe"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
@@ -1344,10 +1498,6 @@ builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
-bytes@2.4.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.4.0.tgz#7d97196f9d5baf7f6935e25985549edd2a6c2339"
-
bytes@2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-2.5.0.tgz#4c9423ea2d252c270c41b2bdefeff9bb6b62c06a"
@@ -1356,6 +1506,38 @@ bytes@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+cacache@^10.0.1:
+ version "10.0.4"
+ resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460"
+ dependencies:
+ bluebird "^3.5.1"
+ chownr "^1.0.1"
+ glob "^7.1.2"
+ graceful-fs "^4.1.11"
+ lru-cache "^4.1.1"
+ mississippi "^2.0.0"
+ mkdirp "^0.5.1"
+ move-concurrently "^1.0.1"
+ promise-inflight "^1.0.1"
+ rimraf "^2.6.2"
+ ssri "^5.2.4"
+ unique-filename "^1.1.0"
+ y18n "^4.0.0"
+
+cache-base@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2"
+ dependencies:
+ collection-visit "^1.0.0"
+ component-emitter "^1.2.1"
+ get-value "^2.0.6"
+ has-value "^1.0.0"
+ isobject "^3.0.1"
+ set-value "^2.0.0"
+ to-object-path "^0.3.0"
+ union-value "^1.0.0"
+ unset-value "^1.0.0"
+
cached-path-relative@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/cached-path-relative/-/cached-path-relative-1.0.1.tgz#d09c4b52800aa4c078e2dd81a869aac90d2e54e7"
@@ -1393,7 +1575,7 @@ camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
-camelcase@^4.1.0:
+camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -1410,6 +1592,10 @@ caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
version "1.0.30000649"
resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000649.tgz#1ee1754a6df235450c8b7cd15e0ebf507221a86a"
+capture-stack-trace@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.0.tgz#4a6fa07399c26bba47f0b2496b4d0fb408c5550d"
+
caseless@~0.11.0:
version "0.11.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7"
@@ -1443,7 +1629,27 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0:
escape-string-regexp "^1.0.5"
supports-color "^4.0.0"
-chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.7.0:
+chalk@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.1.tgz#523fe2678aec7b04e8041909292fe8b17059b796"
+ dependencies:
+ ansi-styles "^3.2.0"
+ escape-string-regexp "^1.0.5"
+ supports-color "^5.2.0"
+
+chardet@^0.4.0:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
+
+chart.js@1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/chart.js/-/chart.js-1.0.2.tgz#ad57d2229cfd8ccf5955147e8121b4911e69dfe7"
+
+check-types@^7.3.0:
+ version "7.3.0"
+ resolved "https://registry.yarnpkg.com/check-types/-/check-types-7.3.0.tgz#468f571a4435c24248f5fd0cb0e8d87c3c341e7d"
+
+chokidar@^1.4.1, chokidar@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468"
dependencies:
@@ -1458,19 +1664,42 @@ chokidar@^1.4.1, chokidar@^1.4.3, chokidar@^1.6.0, chokidar@^1.7.0:
optionalDependencies:
fsevents "^1.0.0"
-cipher-base@^1.0.0, cipher-base@^1.0.1:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.3.tgz#eeabf194419ce900da3018c207d212f2a6df0a07"
+chokidar@^2.0.0, chokidar@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7"
+ dependencies:
+ anymatch "^2.0.0"
+ async-each "^1.0.0"
+ braces "^2.3.0"
+ glob-parent "^3.1.0"
+ inherits "^2.0.1"
+ is-binary-path "^1.0.0"
+ is-glob "^4.0.0"
+ normalize-path "^2.1.1"
+ path-is-absolute "^1.0.0"
+ readdirp "^2.0.0"
+ upath "^1.0.0"
+ optionalDependencies:
+ fsevents "^1.0.0"
+
+chownr@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181"
+
+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"
dependencies:
inherits "^2.0.1"
+ safe-buffer "^5.0.1"
circular-json@^0.3.1:
version "0.3.3"
resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66"
-circular-json@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.4.0.tgz#c448ea998b7fe31ecf472ec29c6b608e2e2a62fd"
+circular-json@^0.5.1:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.1.tgz#b8942a09e535863dc21b04417a91971e1d9cd91f"
clap@^1.0.9:
version "1.1.3"
@@ -1478,16 +1707,35 @@ clap@^1.0.9:
dependencies:
chalk "^1.1.3"
+class-utils@^0.3.5:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
+ dependencies:
+ arr-union "^3.1.0"
+ define-property "^0.2.5"
+ isobject "^3.0.0"
+ static-extend "^0.1.1"
+
classlist-polyfill@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e"
+cli-boxes@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143"
+
cli-cursor@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-1.0.2.tgz#64da3f7d56a54412e59794bd62dc35295e8f2987"
dependencies:
restore-cursor "^1.0.1"
+cli-cursor@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
+ dependencies:
+ restore-cursor "^2.0.0"
+
cli-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
@@ -1538,6 +1786,13 @@ code-point-at@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
+collection-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
+ dependencies:
+ map-visit "^1.0.0"
+ object-visit "^1.0.0"
+
color-convert@^1.3.0, color-convert@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
@@ -1589,17 +1844,24 @@ combine-source-map@~0.7.1:
lodash.memoize "~3.0.3"
source-map "~0.5.3"
-combined-stream@^1.0.5, combined-stream@~1.0.5:
- version "1.0.5"
- resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009"
+combine-source-map@~0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/combine-source-map/-/combine-source-map-0.8.0.tgz#a58d0df042c186fcf822a8e8015f5450d2d79a8b"
dependencies:
- delayed-stream "~1.0.0"
+ convert-source-map "~1.1.0"
+ inline-source-map "~0.6.0"
+ lodash.memoize "~3.0.3"
+ source-map "~0.5.3"
-commander@^2.9.0:
- version "2.9.0"
- resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4"
+combined-stream@1.0.6, combined-stream@^1.0.5, combined-stream@~1.0.5:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818"
dependencies:
- graceful-readlink ">= 1.0.0"
+ delayed-stream "~1.0.0"
+
+commander@^2.13.0, commander@^2.9.0:
+ version "2.14.1"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
commondir@^1.0.1:
version "1.0.1"
@@ -1609,7 +1871,7 @@ component-bind@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1"
-component-emitter@1.2.1:
+component-emitter@1.2.1, component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
@@ -1623,11 +1885,14 @@ compressible@~2.0.10:
dependencies:
mime-db ">= 1.29.0 < 2"
-compression-webpack-plugin@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.0.0.tgz#5c5eb6afd08ca6a5d66006eeef71da17b01bd676"
+compression-webpack-plugin@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/compression-webpack-plugin/-/compression-webpack-plugin-1.1.7.tgz#b0dfb97cf1d26baab997b584b8c36fe91872abe2"
dependencies:
- async "2.4.1"
+ async "^2.4.1"
+ cacache "^10.0.1"
+ find-cache-dir "^1.0.0"
+ serialize-javascript "^1.4.0"
webpack-sources "^1.0.1"
compression@^1.5.2:
@@ -1646,7 +1911,7 @@ concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-concat-stream@^1.5.2:
+concat-stream@^1.5.0, concat-stream@^1.5.2:
version "1.6.0"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.0.tgz#0aac662fd52be78964d5532f694784e70110acf7"
dependencies:
@@ -1662,31 +1927,29 @@ concat-stream@~1.5.0, concat-stream@~1.5.1:
readable-stream "~2.0.0"
typedarray "~0.0.5"
-configstore@^1.0.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/configstore/-/configstore-1.4.0.tgz#c35781d0501d268c25c54b8b17f6240e8a4fb021"
+configstore@^3.0.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/configstore/-/configstore-3.1.1.tgz#094ee662ab83fad9917678de114faaea8fcdca90"
dependencies:
+ dot-prop "^4.1.0"
graceful-fs "^4.1.2"
- mkdirp "^0.5.0"
- object-assign "^4.0.1"
- os-tmpdir "^1.0.0"
- osenv "^0.1.0"
- uuid "^2.0.1"
- write-file-atomic "^1.1.2"
- xdg-basedir "^2.0.0"
+ make-dir "^1.0.0"
+ unique-string "^1.0.0"
+ write-file-atomic "^2.0.0"
+ xdg-basedir "^3.0.0"
connect-history-api-fallback@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.3.0.tgz#e51d17f8f0ef0db90a64fdb47de3051556e9f169"
connect@^3.6.0:
- version "3.6.3"
- resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.3.tgz#f7320d46a25b4be7b483a2236517f24b1e27e301"
+ version "3.6.6"
+ resolved "https://registry.yarnpkg.com/connect/-/connect-3.6.6.tgz#09eff6c55af7236e137135a72574858b6786f524"
dependencies:
- debug "2.6.8"
- finalhandler "1.0.4"
- parseurl "~1.3.1"
- utils-merge "1.0.0"
+ debug "2.6.9"
+ finalhandler "1.1.0"
+ parseurl "~1.3.2"
+ utils-merge "1.0.1"
console-browserify@^1.1.0:
version "1.1.0"
@@ -1716,9 +1979,9 @@ content-disposition@0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4"
-content-type@~1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.2.tgz#b7d113aee7a8dd27bd21133c4dc2529df1721eed"
+content-type@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
convert-source-map@^1.5.0:
version "1.5.0"
@@ -1736,27 +1999,46 @@ cookie@0.3.1:
version "0.3.1"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb"
-copy-webpack-plugin@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.0.1.tgz#9728e383b94316050d0c7463958f2b85c0aa8200"
+copy-concurrently@^1.0.0:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0"
dependencies:
- bluebird "^2.10.2"
- fs-extra "^0.26.4"
- glob "^6.0.4"
- is-glob "^3.1.0"
+ aproba "^1.1.1"
+ fs-write-stream-atomic "^1.0.8"
+ iferr "^0.1.5"
+ mkdirp "^0.5.1"
+ rimraf "^2.5.4"
+ run-queue "^1.0.0"
+
+copy-descriptor@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
+
+copy-webpack-plugin@^4.4.1:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.4.1.tgz#1e8c366211db6dc2ddee40e5a3e4fc661dd149e8"
+ dependencies:
+ cacache "^10.0.1"
+ find-cache-dir "^1.0.0"
+ globby "^7.1.1"
+ is-glob "^4.0.0"
loader-utils "^0.2.15"
- lodash "^4.3.0"
- minimatch "^3.0.0"
- node-dir "^0.1.10"
+ minimatch "^3.0.4"
+ p-limit "^1.0.0"
+ serialize-javascript "^1.4.0"
-core-js@^2.2.0, core-js@^2.4.1:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
+core-js@^2.2.0:
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
core-js@^2.4.0, core-js@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b"
+core-js@^2.4.1:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e"
+
core-js@~2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.3.0.tgz#fab83fbb0b2d8dc85fa636c4b9d34c75420c6d65"
@@ -1783,21 +2065,31 @@ create-ecdh@^4.0.0:
bn.js "^4.1.0"
elliptic "^6.0.0"
-create-hash@^1.1.0, create-hash@^1.1.1:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.2.tgz#51210062d7bb7479f6c65bb41a92208b1d61abad"
+create-error-class@^3.0.0:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/create-error-class/-/create-error-class-3.0.2.tgz#06be7abef947a3f14a30fd610671d401bca8b7b6"
+ dependencies:
+ capture-stack-trace "^1.0.0"
+
+create-hash@^1.1.0, create-hash@^1.1.2:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd"
dependencies:
cipher-base "^1.0.1"
inherits "^2.0.1"
- ripemd160 "^1.0.0"
- sha.js "^2.3.6"
+ ripemd160 "^2.0.0"
+ sha.js "^2.4.0"
-create-hmac@^1.1.0, create-hmac@^1.1.2:
- version "1.1.4"
- resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.4.tgz#d3fb4ba253eb8b3f56e39ea2fbcb8af747bd3170"
+create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06"
dependencies:
+ cipher-base "^1.0.3"
create-hash "^1.1.0"
inherits "^2.0.1"
+ ripemd160 "^2.0.0"
+ safe-buffer "^5.0.1"
+ sha.js "^2.4.8"
cropper@^2.3.0:
version "2.3.0"
@@ -1805,7 +2097,7 @@ cropper@^2.3.0:
dependencies:
jquery ">= 1.9.1"
-cross-spawn@^5.0.1:
+cross-spawn@5.1.0, cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
dependencies:
@@ -1856,34 +2148,32 @@ crypto-browserify@^3.11.0:
public-encrypt "^4.0.0"
randombytes "^2.0.0"
+crypto-random-string@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e"
+
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
-css-loader@^0.28.0:
- version "0.28.0"
- resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.0.tgz#417cfa9789f8cde59a30ccbf3e4da7a806889bad"
+css-loader@^0.28.9:
+ version "0.28.9"
+ resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.9.tgz#68064b85f4e271d7ce4c48a58300928e535d1c95"
dependencies:
- babel-code-frame "^6.11.0"
+ babel-code-frame "^6.26.0"
css-selector-tokenizer "^0.7.0"
- cssnano ">=2.6.1 <4"
+ cssnano "^3.10.0"
+ icss-utils "^2.1.0"
loader-utils "^1.0.2"
lodash.camelcase "^4.3.0"
- object-assign "^4.0.1"
+ object-assign "^4.1.1"
postcss "^5.0.6"
- postcss-modules-extract-imports "^1.0.0"
- postcss-modules-local-by-default "^1.0.1"
- postcss-modules-scope "^1.0.0"
- postcss-modules-values "^1.1.0"
- source-list-map "^0.1.7"
-
-css-selector-tokenizer@^0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.6.0.tgz#6445f582c7930d241dcc5007a43d6fcb8f073152"
- dependencies:
- cssesc "^0.1.0"
- fastparse "^1.1.1"
- regexpu-core "^1.0.0"
+ postcss-modules-extract-imports "^1.2.0"
+ postcss-modules-local-by-default "^1.2.0"
+ postcss-modules-scope "^1.1.0"
+ postcss-modules-values "^1.3.0"
+ postcss-value-parser "^3.3.0"
+ source-list-map "^2.0.0"
css-selector-tokenizer@^0.7.0:
version "0.7.0"
@@ -1897,7 +2187,7 @@ cssesc@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4"
-"cssnano@>=2.6.1 <4":
+cssnano@^3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38"
dependencies:
@@ -1951,6 +2241,10 @@ custom-event@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425"
+cyclist@~0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640"
+
d3-array@^1.2.0, d3-array@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc"
@@ -2107,7 +2401,7 @@ de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
-debug@2, debug@~2.6.4, debug@~2.6.6, debug@~2.6.9:
+debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@~2.6.4, debug@~2.6.6:
version "2.6.9"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
dependencies:
@@ -2119,19 +2413,13 @@ debug@2.2.0, debug@~2.2.0:
dependencies:
ms "0.7.1"
-debug@2.6.7:
- version "2.6.7"
- resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.7.tgz#92bad1f6d05bbb6bba22cca88bcd0ec894c2861e"
- dependencies:
- ms "2.0.0"
-
-debug@2.6.8, debug@^2.1.0, debug@^2.1.1, debug@^2.2.0, debug@^2.6.6, debug@^2.6.8:
+debug@2.6.8, debug@^2.1.1, debug@^2.6.6, debug@^2.6.8:
version "2.6.8"
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
ms "2.0.0"
-debug@^3.0.1, debug@^3.1.0:
+debug@^3.0.1, debug@^3.1.0, debug@~3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
dependencies:
@@ -2145,13 +2433,17 @@ deckar01-task_list@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/deckar01-task_list/-/deckar01-task_list-2.0.0.tgz#7f7a595430d21b3036ed5dfbf97d6b65de18e2c9"
+decode-uri-component@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
+
decompress-response@^3.2.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
dependencies:
mimic-response "^1.0.0"
-deep-equal@^1.0.1, deep-equal@~1.0.1:
+deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
@@ -2176,7 +2468,26 @@ define-properties@^1.1.2:
foreach "^2.0.5"
object-keys "^1.0.8"
-defined@^1.0.0, defined@~1.0.0:
+define-property@^0.2.5:
+ version "0.2.5"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
+ dependencies:
+ is-descriptor "^0.1.0"
+
+define-property@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6"
+ dependencies:
+ is-descriptor "^1.0.0"
+
+define-property@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d"
+ dependencies:
+ is-descriptor "^1.0.2"
+ isobject "^3.0.1"
+
+defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
@@ -2223,11 +2534,7 @@ delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
-depd@1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.0.tgz#e1bd82c6aab6ced965b97b88b17ed3e528ca18c3"
-
-depd@1.1.1, depd@~1.1.0, depd@~1.1.1:
+depd@1.1.1, depd@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359"
@@ -2257,10 +2564,21 @@ detect-indent@^4.0.0:
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"
+
detect-node@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127"
+detect-port-alt@1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.5.tgz#a1aa8fc805a4a5df9b905b7ddc7eed036bcce889"
+ dependencies:
+ address "^1.0.1"
+ debug "^2.6.0"
+
detective@^4.0.0:
version "4.7.1"
resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e"
@@ -2284,6 +2602,13 @@ diffie-hellman@^5.0.0:
miller-rabin "^4.0.0"
randombytes "^2.0.0"
+dir-glob@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034"
+ dependencies:
+ arrify "^1.0.1"
+ path-type "^3.0.0"
+
dns-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
@@ -2360,6 +2685,12 @@ domutils@^1.5.1:
dom-serializer "0"
domelementtype "1"
+dot-prop@^4.1.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
+ dependencies:
+ is-obj "^1.0.0"
+
double-ended-queue@^2.1.0-0:
version "2.1.0-0"
resolved "https://registry.yarnpkg.com/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz#103d3527fd31528f40188130c841efdd78264e5c"
@@ -2382,9 +2713,9 @@ duplexer@^0.1.1, duplexer@~0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
-duplexify@^3.2.0:
- version "3.5.1"
- resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.1.tgz#4e1516be68838bc90a49994f0b39a6e5960befcd"
+duplexify@^3.4.2, duplexify@^3.5.3:
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.5.3.tgz#8b5818800df92fd0125b27ab896491912858243e"
dependencies:
end-of-stream "^1.0.0"
inherits "^2.0.1"
@@ -2401,22 +2732,25 @@ ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
-ejs@^2.5.6:
- version "2.5.6"
- resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.6.tgz#479636bfa3fe3b1debd52087f0acb204b4f19c88"
+ejs@^2.5.7:
+ version "2.5.7"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a"
electron-to-chromium@^1.2.7:
version "1.3.3"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.3.tgz#651eb63fe89f39db70ffc8dbd5d9b66958bc6a0e"
elliptic@^6.0.0:
- version "6.3.3"
- resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.3.3.tgz#5482d9646d54bcb89fd7d994fc9e2e9568876e3f"
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
dependencies:
bn.js "^4.4.0"
brorand "^1.0.1"
hash.js "^1.0.0"
+ hmac-drbg "^1.0.0"
inherits "^2.0.1"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.0"
emoji-unicode-version@^0.2.1:
version "0.2.1"
@@ -2427,8 +2761,8 @@ emojis-list@^2.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
encodeurl@~1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
end-of-stream@^1.0.0:
version "1.4.0"
@@ -2436,13 +2770,19 @@ end-of-stream@^1.0.0:
dependencies:
once "^1.4.0"
+end-of-stream@^1.1.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
+ dependencies:
+ once "^1.4.0"
+
engine.io-client@~3.1.0:
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.4.tgz#4fcf1370b47163bd2ce9be2733972430350d4ea1"
+ version "3.1.5"
+ resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.1.5.tgz#85de17666560327ef1817978f6e3f8101ded2c47"
dependencies:
component-emitter "1.2.1"
component-inherit "0.0.3"
- debug "~2.6.9"
+ debug "~3.1.0"
engine.io-parser "~2.1.1"
has-cors "1.1.0"
indexof "0.0.1"
@@ -2463,17 +2803,17 @@ engine.io-parser@~2.1.0, engine.io-parser@~2.1.1:
has-binary2 "~1.0.2"
engine.io@~3.1.0:
- version "3.1.4"
- resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.4.tgz#3d0211b70a552ce841ffc7da8627b301a9a4162e"
+ version "3.1.5"
+ resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.1.5.tgz#0e7ef9d690eb0b35597f1d4ad02a26ca2dba3845"
dependencies:
- accepts "1.3.3"
+ accepts "~1.3.4"
base64id "1.0.0"
cookie "0.3.1"
- debug "~2.6.9"
+ debug "~3.1.0"
engine.io-parser "~2.1.0"
ws "~3.3.1"
optionalDependencies:
- uws "~0.14.4"
+ uws "~9.14.0"
enhanced-resolve@^3.4.0:
version "3.4.1"
@@ -2512,9 +2852,9 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
-es-abstract@^1.5.0:
- version "1.8.2"
- resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.8.2.tgz#25103263dc4decbda60e0c737ca32313518027ee"
+es-abstract@^1.7.0:
+ version "1.10.0"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
dependencies:
es-to-primitive "^1.1.1"
function-bind "^1.1.1"
@@ -2556,7 +2896,7 @@ es6-map@^0.1.3:
es6-symbol "~3.1.1"
event-emitter "~0.3.5"
-es6-promise@^3.0.2, es6-promise@~3.0.2:
+es6-promise@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-3.0.2.tgz#010d5858423a5f118979665f46486a95c6ee2bb6"
@@ -2810,9 +3150,9 @@ esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
-etag@~1.8.0:
- version "1.8.0"
- resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.0.tgz#6f631aef336d6c46362b51764044ce216be3c051"
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
eve-raphael@0.5.0:
version "0.5.0"
@@ -2845,17 +3185,18 @@ events@^1.0.0, events@~1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
-eventsource@0.1.6, eventsource@^0.1.3:
+eventsource@0.1.6:
version "0.1.6"
resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232"
dependencies:
original ">=0.0.5"
-evp_bytestokey@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.0.tgz#497b66ad9fef65cd7c08a6180824ba1476b66e53"
+evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
dependencies:
- create-hash "^1.1.1"
+ md5.js "^1.3.4"
+ safe-buffer "^5.1.1"
execa@^0.7.0:
version "0.7.0"
@@ -2887,6 +3228,18 @@ expand-brackets@^0.1.4:
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"
+ dependencies:
+ debug "^2.3.3"
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ posix-character-classes "^0.1.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
expand-range@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-0.1.1.tgz#4cb8eda0993ca56fa4f41fc42f3cbb4ccadff044"
@@ -2900,60 +3253,106 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
-exports-loader@^0.6.4:
- version "0.6.4"
- resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.4.tgz#d70fc6121975b35fc12830cf52754be2740fc886"
+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"
dependencies:
- loader-utils "^1.0.2"
- source-map "0.5.x"
+ homedir-polyfill "^1.0.1"
-express@^4.13.3, express@^4.15.2:
- version "4.15.4"
- resolved "https://registry.yarnpkg.com/express/-/express-4.15.4.tgz#032e2253489cf8fce02666beca3d11ed7a2daed1"
+exports-loader@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5"
dependencies:
- accepts "~1.3.3"
+ loader-utils "^1.1.0"
+ source-map "0.5.0"
+
+express@^4.16.2:
+ version "4.16.2"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c"
+ dependencies:
+ accepts "~1.3.4"
array-flatten "1.1.1"
+ body-parser "1.18.2"
content-disposition "0.5.2"
- content-type "~1.0.2"
+ content-type "~1.0.4"
cookie "0.3.1"
cookie-signature "1.0.6"
- debug "2.6.8"
+ debug "2.6.9"
depd "~1.1.1"
encodeurl "~1.0.1"
escape-html "~1.0.3"
- etag "~1.8.0"
- finalhandler "~1.0.4"
- fresh "0.5.0"
+ etag "~1.8.1"
+ finalhandler "1.1.0"
+ fresh "0.5.2"
merge-descriptors "1.0.1"
methods "~1.1.2"
on-finished "~2.3.0"
- parseurl "~1.3.1"
+ parseurl "~1.3.2"
path-to-regexp "0.1.7"
- proxy-addr "~1.1.5"
- qs "6.5.0"
+ proxy-addr "~2.0.2"
+ qs "6.5.1"
range-parser "~1.2.0"
- send "0.15.4"
- serve-static "1.12.4"
- setprototypeof "1.0.3"
+ safe-buffer "5.1.1"
+ send "0.16.1"
+ serve-static "1.13.1"
+ setprototypeof "1.1.0"
statuses "~1.3.1"
type-is "~1.6.15"
- utils-merge "1.0.0"
- vary "~1.1.1"
+ utils-merge "1.0.1"
+ vary "~1.1.2"
+
+extend-shallow@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f"
+ dependencies:
+ is-extendable "^0.1.0"
+
+extend-shallow@^3.0.0, extend-shallow@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8"
+ dependencies:
+ assign-symbols "^1.0.0"
+ is-extendable "^1.0.1"
extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
+external-editor@^2.0.4:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48"
+ dependencies:
+ chardet "^0.4.0"
+ iconv-lite "^0.4.17"
+ tmp "^0.0.33"
+
extglob@^0.3.1:
version "0.3.2"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
dependencies:
is-extglob "^1.0.0"
-extsprintf@1.3.0, extsprintf@^1.2.0:
+extglob@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
+ dependencies:
+ array-unique "^0.3.2"
+ define-property "^1.0.0"
+ expand-brackets "^2.1.4"
+ extend-shallow "^2.0.1"
+ fragment-cache "^0.2.1"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
+extsprintf@1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
+extsprintf@^1.2.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
+
fast-deep-equal@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz#96256a3bc975595eb36d82e9929d060d893439ff"
@@ -2982,12 +3381,6 @@ faye-websocket@~0.11.0:
dependencies:
websocket-driver ">=0.5.1"
-faye-websocket@~0.7.3:
- version "0.7.3"
- resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.7.3.tgz#cc4074c7f4a4dfd03af54dd65c354b135132ce11"
- dependencies:
- websocket-driver ">=0.3.6"
-
figures@^1.3.5:
version "1.7.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
@@ -2995,6 +3388,12 @@ figures@^1.3.5:
escape-string-regexp "^1.0.5"
object-assign "^4.1.0"
+figures@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
file-entry-cache@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
@@ -3002,19 +3401,20 @@ file-entry-cache@^2.0.0:
flat-cache "^1.2.1"
object-assign "^4.0.1"
-file-loader@^0.11.1:
- version "0.11.1"
- resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-0.11.1.tgz#6b328ee1234a729e4e47d36375dd6d35c0e1db84"
+file-loader@^1.1.8:
+ version "1.1.8"
+ resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.8.tgz#a62592ed732667d7482dc3268c381c7f0c913086"
dependencies:
loader-utils "^1.0.2"
+ schema-utils "^0.4.5"
file-uri-to-path@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
filename-regex@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775"
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
fileset@^2.0.2:
version "2.0.3"
@@ -3023,13 +3423,13 @@ fileset@^2.0.2:
glob "^7.0.3"
minimatch "^3.0.3"
-filesize@3.3.0:
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.3.0.tgz#53149ea3460e3b2e024962a51648aa572cf98122"
+filesize@3.5.11:
+ version "3.5.11"
+ resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.11.tgz#1919326749433bb3cf77368bd158caabcc19e9ee"
-filesize@^3.5.9:
- version "3.5.10"
- resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.5.10.tgz#fc8fa23ddb4ef9e5e0ab6e1e64f679a24a56761f"
+filesize@^3.5.11:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.0.tgz#22d079615624bb6fd3c04026120628a41b3f4efa"
fill-range@^2.1.0:
version "2.2.3"
@@ -3041,15 +3441,24 @@ fill-range@^2.1.0:
repeat-element "^1.1.2"
repeat-string "^1.5.2"
-finalhandler@1.0.4, finalhandler@~1.0.4:
- version "1.0.4"
- resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.0.4.tgz#18574f2e7c4b98b8ae3b230c21f201f31bdb3fb7"
+fill-range@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"
dependencies:
- debug "2.6.8"
+ extend-shallow "^2.0.1"
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
+ to-regex-range "^2.1.0"
+
+finalhandler@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5"
+ dependencies:
+ debug "2.6.9"
encodeurl "~1.0.1"
escape-html "~1.0.3"
on-finished "~2.3.0"
- parseurl "~1.3.1"
+ parseurl "~1.3.2"
statuses "~1.3.1"
unpipe "~1.0.0"
@@ -3091,6 +3500,13 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
+flush-write-stream@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.2.tgz#c81b90d8746766f1a609a46809946c45dd8ae417"
+ dependencies:
+ inherits "^2.0.1"
+ readable-stream "^2.0.4"
+
follow-redirects@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.0.0.tgz#8e34298cbd2e176f254effec75a1c78cc849fd37"
@@ -3103,21 +3519,15 @@ follow-redirects@^1.2.5:
dependencies:
debug "^3.1.0"
-for-each@~0.3.2:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.2.tgz#2c40450b9348e97f281322593ba96704b9abd4d4"
- dependencies:
- is-function "~1.0.0"
-
-for-in@^0.1.5:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/for-in/-/for-in-0.1.6.tgz#c9f96e89bfad18a545af5ec3ed352a1d9e5b4dc8"
+for-in@^1.0.1, for-in@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
for-own@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.4.tgz#0149b41a39088c7515f51ebe1c1386d45f935072"
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
dependencies:
- for-in "^0.1.5"
+ for-in "^1.0.1"
foreach@^2.0.5:
version "2.0.5"
@@ -3144,20 +3554,33 @@ form-data@~2.1.1:
mime-types "^2.1.12"
form-data@~2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.1.tgz#6fb94fbd71885306d73d15cc497fe4cc4ecd44bf"
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099"
dependencies:
asynckit "^0.4.0"
- combined-stream "^1.0.5"
+ combined-stream "1.0.6"
mime-types "^2.1.12"
-forwarded@~0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.0.tgz#19ef9874c4ae1c297bcf078fde63a09b66a84363"
+forwarded@~0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
-fresh@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.0.tgz#f474ca5e6a9246d6fd8e0953cfa9b9c805afa78e"
+fragment-cache@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
+ dependencies:
+ map-cache "^0.2.2"
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+
+from2@^2.1.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
+ dependencies:
+ inherits "^2.0.1"
+ readable-stream "^2.0.0"
from@~0:
version "0.1.7"
@@ -3169,26 +3592,25 @@ fs-access@^1.0.0:
dependencies:
null-check "^1.0.0"
-fs-extra@^0.26.4:
- version "0.26.7"
- resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9"
+fs-write-stream-atomic@^1.0.8:
+ version "1.0.10"
+ resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
dependencies:
graceful-fs "^4.1.2"
- jsonfile "^2.1.0"
- klaw "^1.0.0"
- path-is-absolute "^1.0.0"
- rimraf "^2.2.8"
+ iferr "^0.1.5"
+ imurmurhash "^0.1.4"
+ readable-stream "1 || 2"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
fsevents@^1.0.0:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4"
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8"
dependencies:
nan "^2.3.0"
- node-pre-gyp "^0.6.36"
+ node-pre-gyp "^0.6.39"
fstream-ignore@^1.0.5:
version "1.0.5"
@@ -3214,11 +3636,7 @@ ftp@~0.3.10:
readable-stream "1.1.x"
xregexp "2.0.0"
-function-bind@^1.0.2, function-bind@~1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.0.tgz#16176714c801798e4e8f2cf7f7529467bb4a5771"
-
-function-bind@^1.1.1:
+function-bind@^1.0.2, function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -3272,6 +3690,10 @@ get-uri@2:
ftp "~0.3.10"
readable-stream "2"
+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"
+
getpass@^0.1.1:
version "0.1.7"
resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa"
@@ -3291,6 +3713,13 @@ glob-parent@^2.0.0:
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"
+ dependencies:
+ is-glob "^3.1.0"
+ path-dirname "^1.0.0"
+
glob@^5.0.15:
version "5.0.15"
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
@@ -3301,17 +3730,7 @@ glob@^5.0.15:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^6.0.4:
- version "6.0.4"
- resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22"
- dependencies:
- inflight "^1.0.4"
- inherits "2"
- minimatch "2 || 3"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
-glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1:
+glob@^7.0.0, glob@^7.0.3:
version "7.1.1"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8"
dependencies:
@@ -3322,7 +3741,7 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1:
once "^1.3.0"
path-is-absolute "^1.0.0"
-glob@^7.1.0, glob@^7.1.2, glob@~7.1.2:
+glob@^7.0.5, glob@^7.1.0, glob@^7.1.1, glob@^7.1.2:
version "7.1.2"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15"
dependencies:
@@ -3333,6 +3752,30 @@ glob@^7.1.0, glob@^7.1.2, glob@~7.1.2:
once "^1.3.0"
path-is-absolute "^1.0.0"
+global-dirs@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-0.1.1.tgz#b319c0dd4607f353f3be9cca4c72fc148c49f445"
+ dependencies:
+ ini "^1.3.4"
+
+global-modules@1.0.0, global-modules@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea"
+ dependencies:
+ global-prefix "^1.0.1"
+ is-windows "^1.0.1"
+ resolve-dir "^1.0.0"
+
+global-prefix@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe"
+ dependencies:
+ expand-tilde "^2.0.2"
+ homedir-polyfill "^1.0.1"
+ ini "^1.3.4"
+ is-windows "^1.0.1"
+ which "^1.2.14"
+
globals@^10.0.0:
version "10.4.0"
resolved "https://registry.yarnpkg.com/globals/-/globals-10.4.0.tgz#5c477388b128a9e4c5c5d01c7a2aca68c68b2da7"
@@ -3362,26 +3805,38 @@ globby@^6.1.0:
pify "^2.0.0"
pinkie-promise "^2.0.0"
+globby@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680"
+ dependencies:
+ array-union "^1.0.1"
+ dir-glob "^2.0.0"
+ glob "^7.1.2"
+ ignore "^3.3.5"
+ pify "^3.0.0"
+ slash "^1.0.0"
+
good-listener@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
dependencies:
delegate "^3.1.2"
-got@^3.2.0:
- version "3.3.1"
- resolved "https://registry.yarnpkg.com/got/-/got-3.3.1.tgz#e5d0ed4af55fc3eef4d56007769d98192bcb2eca"
+got@^6.7.1:
+ version "6.7.1"
+ resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
dependencies:
- duplexify "^3.2.0"
- infinity-agent "^2.0.0"
+ create-error-class "^3.0.0"
+ duplexer3 "^0.1.4"
+ get-stream "^3.0.0"
is-redirect "^1.0.0"
+ is-retry-allowed "^1.0.0"
is-stream "^1.0.0"
lowercase-keys "^1.0.0"
- nested-error-stacks "^1.0.0"
- object-assign "^3.0.0"
- prepend-http "^1.0.0"
- read-all-stream "^3.0.0"
- timed-out "^2.0.0"
+ safe-buffer "^5.0.1"
+ timed-out "^4.0.0"
+ unzip-response "^2.0.1"
+ url-parse-lax "^1.0.0"
got@^7.1.0:
version "7.1.0"
@@ -3402,26 +3857,29 @@ got@^7.1.0:
url-parse-lax "^1.0.0"
url-to-options "^1.0.1"
-graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
+graceful-fs@^4.1.11, graceful-fs@^4.1.2:
version "4.1.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
-"graceful-readlink@>= 1.0.0":
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725"
-
graphlib@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.1.tgz#42352c52ba2f4d035cb566eb91f7395f76ebc951"
dependencies:
lodash "^4.11.1"
-gzip-size@3.0.0, gzip-size@^3.0.0:
+gzip-size@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-3.0.0.tgz#546188e9bdc337f673772f81660464b389dce520"
dependencies:
duplexer "^0.1.1"
+gzip-size@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c"
+ dependencies:
+ duplexer "^0.1.1"
+ pify "^3.0.0"
+
handle-thing@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4"
@@ -3491,6 +3949,10 @@ has-flag@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
+has-flag@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
+
has-symbol-support-x@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.3.0.tgz#588bd6927eaa0e296afae24160659167fc2be4f8"
@@ -3505,23 +3967,64 @@ has-unicode@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
-has@^1.0.0, has@^1.0.1, has@~1.0.1:
+has-value@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
+ dependencies:
+ get-value "^2.0.3"
+ has-values "^0.1.4"
+ isobject "^2.0.0"
+
+has-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177"
+ dependencies:
+ get-value "^2.0.6"
+ has-values "^1.0.0"
+ isobject "^3.0.0"
+
+has-values@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771"
+
+has-values@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f"
+ dependencies:
+ is-number "^3.0.0"
+ kind-of "^4.0.0"
+
+has@^1.0.0, has@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28"
dependencies:
function-bind "^1.0.2"
+hash-base@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1"
+ dependencies:
+ inherits "^2.0.1"
+
+hash-base@^3.0.0:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
+ dependencies:
+ inherits "^2.0.1"
+ safe-buffer "^5.0.1"
+
hash-sum@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04"
-hash.js@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.0.3.tgz#1332ff00156c0a0ffdd8236013d07b77a0451573"
+hash.js@^1.0.0, hash.js@^1.0.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846"
dependencies:
- inherits "^2.0.1"
+ inherits "^2.0.3"
+ minimalistic-assert "^1.0.0"
-hawk@~3.1.3:
+hawk@3.1.3, hawk@~3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4"
dependencies:
@@ -3550,13 +4053,21 @@ hipchat-notifier@^1.1.0:
lodash "^4.0.0"
request "^2.0.0"
+hmac-drbg@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1"
+ dependencies:
+ hash.js "^1.0.3"
+ minimalistic-assert "^1.0.0"
+ minimalistic-crypto-utils "^1.0.1"
+
hoek@2.x.x:
version "2.16.3"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed"
hoek@4.x.x:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.0.tgz#72d9d0754f7fe25ca2d01ad8f8f9a9449a89526d"
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
home-or-tmp@^2.0.0:
version "2.0.0"
@@ -3565,6 +4076,12 @@ home-or-tmp@^2.0.0:
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"
+ dependencies:
+ parse-passwd "^1.0.0"
+
hosted-git-info@^2.1.4:
version "2.2.0"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.2.0.tgz#7a0d097863d886c0fabbdcd37bf1758d8becf8a5"
@@ -3582,7 +4099,7 @@ html-comment-regex@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e"
-html-entities@1.2.0, html-entities@^1.2.0:
+html-entities@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.0.tgz#41948caf85ce82fed36e4e6a0ed371a6664379e2"
@@ -3605,7 +4122,7 @@ http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
-http-errors@1.6.2, http-errors@~1.6.2:
+http-errors@1.6.2, http-errors@~1.6.1, http-errors@~1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736"
dependencies:
@@ -3614,15 +4131,6 @@ http-errors@1.6.2, http-errors@~1.6.2:
setprototypeof "1.0.3"
statuses ">= 1.3.1 < 2"
-http-errors@~1.6.1:
- version "1.6.1"
- resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.1.tgz#5f8b8ed98aca545656bf572997387f904a722257"
- dependencies:
- depd "1.1.0"
- inherits "2.0.3"
- setprototypeof "1.0.3"
- statuses ">= 1.3.1 < 2"
-
http-proxy-agent@1:
version "1.0.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-1.0.0.tgz#cc1ce38e453bf984a0f7702d2dd59c73d081284a"
@@ -3694,19 +4202,29 @@ iconv-lite@0.4.15:
version "0.4.15"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
-iconv-lite@0.4.19:
+iconv-lite@0.4.19, iconv-lite@^0.4.17:
version "0.4.19"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
-icss-replace-symbols@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.0.2.tgz#cb0b6054eb3af6edc9ab1d62d01933e2d4c8bfa5"
+icss-replace-symbols@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded"
+
+icss-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962"
+ dependencies:
+ postcss "^6.0.1"
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
-ignore-by-default@^1.0.0:
+iferr@^0.1.5:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
+
+ignore-by-default@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
@@ -3714,16 +4232,31 @@ ignore@^3.2.0:
version "3.3.3"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
+ignore@^3.3.5:
+ version "3.3.7"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021"
+
immediate@~3.0.5:
version "3.0.6"
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
-imports-loader@^0.7.1:
- version "0.7.1"
- resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.7.1.tgz#f204b5f34702a32c1db7d48d89d5e867a0441253"
+import-lazy@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43"
+
+import-local@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
+ dependencies:
+ pkg-dir "^2.0.0"
+ resolve-cwd "^2.0.0"
+
+imports-loader@^0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.8.0.tgz#030ea51b8ca05977c40a3abfd9b4088fe0be9a69"
dependencies:
loader-utils "^1.0.2"
- source-map "^0.5.6"
+ source-map "^0.6.1"
imurmurhash@^0.1.4:
version "0.1.4"
@@ -3743,10 +4276,6 @@ indexof@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d"
-infinity-agent@^2.0.0:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/infinity-agent/-/infinity-agent-2.0.3.tgz#45e0e2ff7a9eb030b27d62b74b3744b7a7ac4216"
-
inflection@~1.10.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/inflection/-/inflection-1.10.0.tgz#5bffcb1197ad3e81050f8e17e21668087ee9eb2f"
@@ -3770,9 +4299,9 @@ inherits@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
-ini@~1.3.0:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
+ini@^1.3.4, ini@~1.3.0:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927"
inline-source-map@~0.6.0:
version "0.6.2"
@@ -3780,6 +4309,25 @@ inline-source-map@~0.6.0:
dependencies:
source-map "~0.5.3"
+inquirer@3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9"
+ dependencies:
+ ansi-escapes "^3.0.0"
+ chalk "^2.0.0"
+ cli-cursor "^2.1.0"
+ cli-width "^2.0.0"
+ external-editor "^2.0.4"
+ figures "^2.0.0"
+ lodash "^4.3.0"
+ mute-stream "0.0.7"
+ run-async "^2.2.0"
+ rx-lite "^4.0.8"
+ rx-lite-aggregates "^4.0.8"
+ string-width "^2.1.0"
+ strip-ansi "^4.0.0"
+ through "^2.3.6"
+
inquirer@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
@@ -3811,7 +4359,7 @@ insert-module-globals@^7.0.0:
through2 "^2.0.0"
xtend "^4.0.0"
-internal-ip@^1.2.0:
+internal-ip@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c"
dependencies:
@@ -3839,9 +4387,9 @@ ip@^1.1.0, ip@^1.1.2, ip@^1.1.4, ip@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a"
-ipaddr.js@1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.4.0.tgz#296aca878a821816e5b85d0a285a99bcff4582f0"
+ipaddr.js@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b"
is-absolute-url@^2.0.0:
version "2.1.0"
@@ -3854,6 +4402,18 @@ is-absolute@^0.2.3:
is-relative "^0.2.1"
is-windows "^0.2.0"
+is-accessor-descriptor@^0.1.6:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
+ dependencies:
+ kind-of "^3.0.2"
+
+is-accessor-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656"
+ dependencies:
+ kind-of "^6.0.0"
+
is-arrayish@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
@@ -3864,14 +4424,14 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
-is-buffer@^1.0.2, is-buffer@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
-
is-buffer@^1.1.0:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
+is-buffer@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc"
+
is-builtin-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe"
@@ -3882,13 +4442,41 @@ is-callable@^1.1.1, is-callable@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2"
+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"
+ dependencies:
+ kind-of "^3.0.2"
+
+is-data-descriptor@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7"
+ dependencies:
+ kind-of "^6.0.0"
+
is-date-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16"
-is-dotfile@^1.0.0:
+is-descriptor@^0.1.0:
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
+ dependencies:
+ is-accessor-descriptor "^0.1.6"
+ is-data-descriptor "^0.1.4"
+ kind-of "^5.0.0"
+
+is-descriptor@^1.0.0, is-descriptor@^1.0.2:
version "1.0.2"
- resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d"
+ resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec"
+ dependencies:
+ is-accessor-descriptor "^1.0.0"
+ is-data-descriptor "^1.0.0"
+ kind-of "^6.0.2"
+
+is-dotfile@^1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
is-equal-shallow@^0.1.3:
version "0.1.3"
@@ -3896,15 +4484,21 @@ is-equal-shallow@^0.1.3:
dependencies:
is-primitive "^2.0.0"
-is-extendable@^0.1.1:
+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"
+is-extendable@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4"
+ dependencies:
+ is-plain-object "^2.0.4"
+
is-extglob@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0"
-is-extglob@^2.1.0:
+is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -3924,10 +4518,6 @@ is-fullwidth-code-point@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
-is-function@~1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5"
-
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"
@@ -3940,6 +4530,23 @@ is-glob@^3.1.0:
dependencies:
is-extglob "^2.1.0"
+is-glob@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0"
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-installed-globally@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80"
+ dependencies:
+ global-dirs "^0.1.0"
+ is-path-inside "^1.0.0"
+
+is-my-ip-valid@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824"
+
is-my-json-valid@^2.10.0:
version "2.16.0"
resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693"
@@ -3950,11 +4557,12 @@ is-my-json-valid@^2.10.0:
xtend "^4.0.0"
is-my-json-valid@^2.12.4:
- version "2.17.1"
- resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.1.tgz#3da98914a70a22f0a8563ef1511a246c6fc55471"
+ version "2.17.2"
+ resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c"
dependencies:
generate-function "^2.0.0"
generate-object-property "^1.1.0"
+ is-my-ip-valid "^1.0.0"
jsonpointer "^4.0.0"
xtend "^4.0.0"
@@ -3966,16 +4574,36 @@ is-number@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
-is-number@^2.0.2, is-number@^2.1.0:
+is-number@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
dependencies:
kind-of "^3.0.2"
+is-number@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195"
+ dependencies:
+ kind-of "^3.0.2"
+
+is-number@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff"
+
+is-obj@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
+
is-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
+is-odd@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24"
+ dependencies:
+ is-number "^4.0.0"
+
is-path-cwd@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
@@ -3996,6 +4624,12 @@ is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
+is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ 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"
@@ -4004,6 +4638,10 @@ is-primitive@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
+is-promise@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
is-property@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
@@ -4034,6 +4672,10 @@ is-retry-allowed@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
+is-root@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-root/-/is-root-1.0.0.tgz#07b6c233bc394cd9d02ba15c966bd6660d6342d5"
+
is-stream@^1.0.0, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -4066,6 +4708,14 @@ is-windows@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c"
+is-windows@^1.0.1, is-windows@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
+
+is-wsl@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d"
+
isarray@0.0.1, isarray@~0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
@@ -4086,12 +4736,20 @@ isexe@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-1.1.2.tgz#36f3e22e60750920f5e7241a476a8c6a42275ad0"
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+
isobject@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89"
dependencies:
isarray "1.0.0"
+isobject@^3.0.0, isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -4203,9 +4861,13 @@ jquery-ujs@1.2.2:
dependencies:
jquery ">=1.8.0"
-"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^2.2.4:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02"
+jquery.waitforimages@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b"
+
+"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^3.2.1:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
js-base64@^2.1.9:
version "2.1.9"
@@ -4281,12 +4943,6 @@ json5@^0.5.0, json5@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
-jsonfile@^2.1.0:
- version "2.4.0"
- resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8"
- optionalDependencies:
- graceful-fs "^4.1.6"
-
jsonify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
@@ -4329,9 +4985,9 @@ karma-chrome-launcher@^2.2.0:
fs-access "^1.0.0"
which "^1.2.1"
-karma-coverage-istanbul-reporter@^1.3.3:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.3.3.tgz#daf26051d5a0daa5838a4ce81aa4a41724bdf36b"
+karma-coverage-istanbul-reporter@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-1.4.1.tgz#2b42d145ddbb4868d85d52888c495a21c61d873c"
dependencies:
istanbul-api "^1.1.14"
minimatch "^3.0.4"
@@ -4397,17 +5053,35 @@ karma@^2.0.0:
tmp "0.0.33"
useragent "^2.1.12"
-kind-of@^3.0.2:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47"
+katex@^0.8.3:
+ version "0.8.3"
+ resolved "https://registry.yarnpkg.com/katex/-/katex-0.8.3.tgz#909d99864baf964c3ccae39c4a99a8e0fb9a1bd0"
dependencies:
- is-buffer "^1.0.2"
+ match-at "^0.1.0"
-klaw@^1.0.0:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439"
- optionalDependencies:
- graceful-fs "^4.1.9"
+killable@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b"
+
+kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
+ version "3.2.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64"
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57"
+ dependencies:
+ is-buffer "^1.1.5"
+
+kind-of@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d"
+
+kind-of@^6.0.0, kind-of@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051"
labeled-stream-splicer@^2.0.0:
version "2.0.0"
@@ -4417,16 +5091,22 @@ labeled-stream-splicer@^2.0.0:
isarray "~0.0.1"
stream-splicer "^2.0.0"
-latest-version@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-1.0.1.tgz#72cfc46e3e8d1be651e1ebb54ea9f6ea96f374bb"
+latest-version@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15"
dependencies:
- package-json "^1.0.0"
+ package-json "^4.0.0"
lazy-cache@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e"
+lazy-cache@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264"
+ dependencies:
+ set-getter "^0.1.0"
+
lcid@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835"
@@ -4515,55 +5195,16 @@ locate-path@^2.0.0:
p-locate "^2.0.0"
path-exists "^3.0.0"
-lodash._baseassign@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz#8c38a099500f215ad09e59f1722fd0c52bfe0a4e"
- dependencies:
- lodash._basecopy "^3.0.0"
- lodash.keys "^3.0.0"
-
-lodash._basecopy@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz#8da0e6a876cf344c0ad8a54882111dd3c5c7ca36"
-
lodash._baseget@^3.0.0:
version "3.7.2"
resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4"
-lodash._bindcallback@^3.0.0:
- version "3.0.1"
- resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"
-
-lodash._createassigner@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11"
- dependencies:
- lodash._bindcallback "^3.0.0"
- lodash._isiterateecall "^3.0.0"
- lodash.restparam "^3.0.0"
-
-lodash._getnative@^3.0.0:
- version "3.9.1"
- resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
-
-lodash._isiterateecall@^3.0.0:
- version "3.0.9"
- resolved "https://registry.yarnpkg.com/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz#5203ad7ba425fae842460e696db9cf3e6aac057c"
-
lodash._topath@^3.0.0:
version "3.8.1"
resolved "https://registry.yarnpkg.com/lodash._topath/-/lodash._topath-3.8.1.tgz#3ec5e2606014f4cb97f755fe6914edd8bfc00eac"
dependencies:
lodash.isarray "^3.0.0"
-lodash.assign@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-3.2.0.tgz#3ce9f0234b4b2223e296b8fa0ac1fee8ebca64fa"
- dependencies:
- lodash._baseassign "^3.0.0"
- lodash._createassigner "^3.0.0"
- lodash.keys "^3.0.0"
-
lodash.camelcase@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.1.1.tgz#065b3ff08f0b7662f389934c46a5504c90e0b2d8"
@@ -4592,12 +5233,9 @@ lodash.deburr@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/lodash.deburr/-/lodash.deburr-4.1.0.tgz#ddb1bbb3ef07458c0177ba07de14422cb033ff9b"
-lodash.defaults@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-3.1.2.tgz#c7308b18dbf8bc9372d701a73493c61192bd2e2c"
- dependencies:
- lodash.assign "^3.0.0"
- lodash.restparam "^3.0.0"
+lodash.endswith@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/lodash.endswith/-/lodash.endswith-4.2.1.tgz#fed59ac1738ed3e236edd7064ec456448b37bc09"
lodash.escaperegexp@^4.1.2:
version "4.1.2"
@@ -4610,14 +5248,18 @@ lodash.get@^3.7.0:
lodash._baseget "^3.0.0"
lodash._topath "^3.0.0"
-lodash.isarguments@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
-
lodash.isarray@^3.0.0:
version "3.0.4"
resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
+lodash.isfunction@^3.0.8:
+ version "3.0.9"
+ resolved "https://registry.yarnpkg.com/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz#06de25df4db327ac931981d1bdb067e5af68d051"
+
+lodash.isstring@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
+
lodash.kebabcase@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.0.1.tgz#5e63bc9aa2a5562ff3b97ca7af2f803de1bcb90e"
@@ -4625,14 +5267,6 @@ lodash.kebabcase@4.0.1:
lodash.deburr "^4.0.0"
lodash.words "^4.0.0"
-lodash.keys@^3.0.0:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
- dependencies:
- lodash._getnative "^3.0.0"
- lodash.isarguments "^3.0.0"
- lodash.isarray "^3.0.0"
-
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -4645,10 +5279,6 @@ lodash.mergewith@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.0.tgz#150cf0a16791f5903b8891eab154609274bdea55"
-lodash.restparam@^3.0.0:
- version "3.6.1"
- resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"
-
lodash.snakecase@4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.0.1.tgz#bd012e5d2f93f7b58b9303e9a7fbfd5db13d6281"
@@ -4656,6 +5286,10 @@ lodash.snakecase@4.0.1:
lodash.deburr "^4.0.0"
lodash.words "^4.0.0"
+lodash.startswith@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/lodash.startswith/-/lodash.startswith-4.2.1.tgz#c598c4adce188a27e53145731cdc6c0e7177600c"
+
lodash.uniq@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -4664,7 +5298,7 @@ lodash.words@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.words/-/lodash.words-4.2.0.tgz#5ecfeaf8ecf8acaa8e0c8386295f1993c9cf4036"
-lodash@4.17.4, lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@4.17.4, lodash@^4.11.1, lodash@^4.17.2, lodash@^4.2.0, lodash@^4.3.0:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
@@ -4672,6 +5306,10 @@ lodash@^3.8.0:
version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
+lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.5.0:
+ version "4.17.5"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511"
+
log-symbols@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.1.0.tgz#f35fa60e278832b538dc4dddcbb478a45d3e3be6"
@@ -4679,15 +5317,16 @@ log-symbols@^2.1.0:
chalk "^2.0.1"
log4js@^2.3.9:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.4.1.tgz#b0c4e88133e0e3056afdc6f91f7f377576158778"
+ version "2.5.3"
+ resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1"
dependencies:
- circular-json "^0.4.0"
+ circular-json "^0.5.1"
date-format "^1.2.0"
debug "^3.1.0"
semver "^5.3.0"
streamroller "^0.7.0"
optionalDependencies:
+ amqplib "^0.5.2"
axios "^0.15.3"
hipchat-notifier "^1.1.0"
loggly "^1.1.0"
@@ -4729,11 +5368,7 @@ lowercase-keys@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
-lru-cache@2.2.x:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
-
-lru-cache@^4.0.1, lru-cache@^4.1.1:
+lru-cache@4.1.x, lru-cache@^4.0.1, lru-cache@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55"
dependencies:
@@ -4775,6 +5410,10 @@ make-dir@^1.0.0:
dependencies:
pify "^2.3.0"
+map-cache@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
+
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"
@@ -4783,14 +5422,31 @@ map-stream@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194"
+map-visit@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f"
+ dependencies:
+ object-visit "^1.0.0"
+
marked@^0.3.12:
version "0.3.12"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519"
+match-at@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
+
math-expression-evaluator@^1.2.14:
version "1.2.16"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
+md5.js@^1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d"
+ dependencies:
+ hash-base "^3.0.0"
+ inherits "^2.0.1"
+
media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
@@ -4853,9 +5509,27 @@ micromatch@^2.1.5, micromatch@^2.3.11:
parse-glob "^3.0.4"
regex-cache "^0.4.2"
+micromatch@^3.1.4:
+ version "3.1.6"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.6.tgz#8d7c043b48156f408ca07a4715182b79b99420bf"
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ braces "^2.3.1"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ extglob "^2.0.4"
+ fragment-cache "^0.2.1"
+ kind-of "^6.0.2"
+ nanomatch "^1.2.9"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
+
miller-rabin@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.0.tgz#4a62fb1d42933c05583982f4c716f6fb9e6c6d3d"
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
dependencies:
bn.js "^4.0.0"
brorand "^1.0.1"
@@ -4864,31 +5538,21 @@ miller-rabin@^4.0.0:
version "1.29.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.29.0.tgz#48d26d235589651704ac5916ca06001914266878"
-mime-db@~1.27.0:
- version "1.27.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1"
-
-mime-db@~1.30.0:
- version "1.30.0"
- resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01"
-
-mime-types@^2.1.11, mime-types@~2.1.17:
- version "2.1.17"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.17.tgz#09d7a393f03e995a79f8af857b70a9e0ab16557a"
- dependencies:
- mime-db "~1.30.0"
+mime-db@~1.33.0:
+ version "1.33.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db"
-mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.7:
- version "2.1.15"
- resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed"
+mime-types@^2.1.11, mime-types@^2.1.12, mime-types@~2.1.11, mime-types@~2.1.15, mime-types@~2.1.16, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7:
+ version "2.1.18"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8"
dependencies:
- mime-db "~1.27.0"
+ mime-db "~1.33.0"
-mime@1.3.4, mime@1.3.x, mime@^1.3.4:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
+mime@1.4.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6"
-mime@^1.5.0:
+mime@^1.3.4, mime@^1.4.1, mime@^1.5.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
@@ -4904,6 +5568,10 @@ minimalistic-assert@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
+minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
+
"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
@@ -4916,14 +5584,40 @@ minimatch@3.0.3:
dependencies:
brace-expansion "^1.0.0"
-minimist@0.0.8, minimist@~0.0.1:
+minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0, minimist@~1.2.0:
+minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
+minimist@~0.0.1:
+ version "0.0.10"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf"
+
+mississippi@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f"
+ dependencies:
+ concat-stream "^1.5.0"
+ duplexify "^3.4.2"
+ end-of-stream "^1.1.0"
+ flush-write-stream "^1.0.0"
+ from2 "^2.1.0"
+ parallel-transform "^1.1.0"
+ pump "^2.0.1"
+ pumpify "^1.3.3"
+ stream-each "^1.1.0"
+ through2 "^2.0.0"
+
+mixin-deep@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe"
+ dependencies:
+ for-in "^1.0.2"
+ is-extendable "^1.0.1"
+
mkdirp@0.5.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 "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
@@ -4962,6 +5656,17 @@ mousetrap@^1.4.6:
version "1.4.6"
resolved "https://registry.yarnpkg.com/mousetrap/-/mousetrap-1.4.6.tgz#eaca72e22e56d5b769b7555873b688c3332e390a"
+move-concurrently@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
+ dependencies:
+ aproba "^1.1.1"
+ copy-concurrently "^1.0.0"
+ fs-write-stream-atomic "^1.0.8"
+ mkdirp "^0.5.1"
+ rimraf "^2.5.4"
+ run-queue "^1.0.3"
+
ms@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.1.tgz#9cd13c03adbff25b65effde7ce864ee952017098"
@@ -4985,13 +5690,34 @@ mute-stream@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
+mute-stream@0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+
name-all-modules-plugin@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/name-all-modules-plugin/-/name-all-modules-plugin-1.0.1.tgz#0abfb6ad835718b9fb4def0674e06657a954375c"
nan@^2.3.0:
- version "2.6.2"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.8.0.tgz#ed715f3fe9de02b57a5e6252d90a96675e1f085a"
+
+nanomatch@^1.2.9:
+ version "1.2.9"
+ resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2"
+ dependencies:
+ arr-diff "^4.0.0"
+ array-unique "^0.3.2"
+ define-property "^2.0.2"
+ extend-shallow "^3.0.2"
+ fragment-cache "^0.2.1"
+ is-odd "^2.0.0"
+ is-windows "^1.0.2"
+ kind-of "^6.0.2"
+ object.pick "^1.3.0"
+ regex-not "^1.0.0"
+ snapdragon "^0.8.1"
+ to-regex "^3.0.1"
natural-compare@^1.4.0:
version "1.4.0"
@@ -5001,22 +5727,10 @@ negotiator@0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
-nested-error-stacks@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-1.0.2.tgz#19f619591519f096769a5ba9a86e6eeec823c3cf"
- dependencies:
- inherits "~2.0.1"
-
netmask@~1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35"
-node-dir@^0.1.10:
- version "0.1.17"
- resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5"
- dependencies:
- minimatch "^3.0.2"
-
node-forge@0.6.33:
version "0.6.33"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
@@ -5077,18 +5791,19 @@ node-libs-browser@^2.0.0:
util "^0.10.3"
vm-browserify "0.0.4"
-node-pre-gyp@^0.6.36:
- version "0.6.37"
- resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.37.tgz#3c872b236b2e266e4140578fe1ee88f693323a05"
+node-pre-gyp@^0.6.39:
+ version "0.6.39"
+ resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649"
dependencies:
+ detect-libc "^1.0.2"
+ hawk "3.1.3"
mkdirp "^0.5.1"
nopt "^4.0.1"
npmlog "^4.0.2"
rc "^1.1.7"
- request "^2.81.0"
+ request "2.81.0"
rimraf "^2.6.1"
semver "^5.3.0"
- tape "^4.6.3"
tar "^2.2.1"
tar-pack "^3.4.0"
@@ -5145,20 +5860,19 @@ nodemailer@^2.5.0:
nodemailer-smtp-transport "2.7.2"
socks "1.1.9"
-nodemon@^1.11.0:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.11.0.tgz#226c562bd2a7b13d3d7518b49ad4828a3623d06c"
+nodemon@^1.15.1:
+ version "1.15.1"
+ resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.15.1.tgz#54daa72443d8d5a548f130866b92e65cded0ed58"
dependencies:
- chokidar "^1.4.3"
- debug "^2.2.0"
- es6-promise "^3.0.2"
- ignore-by-default "^1.0.0"
- lodash.defaults "^3.1.2"
- minimatch "^3.0.0"
- ps-tree "^1.0.1"
- touch "1.0.0"
- undefsafe "0.0.3"
- update-notifier "0.5.0"
+ chokidar "^2.0.2"
+ debug "^3.1.0"
+ ignore-by-default "^1.0.1"
+ minimatch "^3.0.4"
+ pstree.remy "^1.1.0"
+ semver "^5.4.1"
+ touch "^3.1.0"
+ undefsafe "^2.0.1"
+ update-notifier "^2.3.0"
nopt@3.x:
version "3.0.6"
@@ -5188,7 +5902,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.0, normalize-path@^2.0.1:
+normalize-path@^2.0.0, normalize-path@^2.0.1, normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9"
dependencies:
@@ -5238,11 +5952,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
-object-assign@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-3.0.0.tgz#9bedd5ca0897949bca47e7ff408062d549f587f2"
-
-object-assign@^4.0.1, object-assign@^4.1.0:
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@@ -5250,14 +5960,24 @@ object-component@0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
-object-inspect@~1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.3.0.tgz#5b1eb8e6742e2ee83342a637034d844928ba2f6d"
+object-copy@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c"
+ dependencies:
+ copy-descriptor "^0.1.0"
+ define-property "^0.2.5"
+ kind-of "^3.0.3"
object-keys@^1.0.8:
version "1.0.11"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d"
+object-visit@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb"
+ dependencies:
+ isobject "^3.0.0"
+
object.omit@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
@@ -5265,6 +5985,12 @@ object.omit@^2.0.0:
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"
+ dependencies:
+ isobject "^3.0.1"
+
obuf@^1.0.0, obuf@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e"
@@ -5279,7 +6005,7 @@ on-headers@~1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7"
-once@1.x, once@^1.3.0, once@^1.3.3, once@^1.4.0:
+once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
dependencies:
@@ -5289,16 +6015,21 @@ onetime@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
+onetime@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
+ dependencies:
+ mimic-fn "^1.0.0"
+
opener@^1.4.3:
version "1.4.3"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
-opn@4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+opn@5.2.0, opn@^5.1.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225"
dependencies:
- object-assign "^4.0.1"
- pinkie-promise "^2.0.0"
+ is-wsl "^1.1.0"
optimist@^0.6.1:
version "0.6.1"
@@ -5350,13 +6081,13 @@ os-locale@^2.0.0:
lcid "^1.0.0"
mem "^1.1.0"
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2:
+os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
-osenv@^0.1.0, osenv@^0.1.4:
- version "0.1.4"
- resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644"
+osenv@^0.1.4:
+ version "0.1.5"
+ resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
dependencies:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
@@ -5369,6 +6100,12 @@ p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
+p-limit@^1.0.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c"
+ dependencies:
+ p-try "^1.0.0"
+
p-limit@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.1.0.tgz#b07ff2d9a5d88bec806035895a2bab66a27988bc"
@@ -5389,6 +6126,10 @@ p-timeout@^1.1.1:
dependencies:
p-finally "^1.0.0"
+p-try@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+
pac-proxy-agent@1:
version "1.1.0"
resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-1.1.0.tgz#34a385dfdf61d2f0ecace08858c745d3e791fd4d"
@@ -5413,12 +6154,14 @@ pac-resolver@~2.0.0:
netmask "~1.0.4"
thunkify "~2.1.1"
-package-json@^1.0.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/package-json/-/package-json-1.2.0.tgz#c8ecac094227cdf76a316874ed05e27cc939a0e0"
+package-json@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
dependencies:
- got "^3.2.0"
- registry-url "^3.0.0"
+ got "^6.7.1"
+ registry-auth-token "^3.0.1"
+ registry-url "^3.0.3"
+ semver "^5.1.0"
pako@~0.2.0:
version "0.2.9"
@@ -5432,6 +6175,14 @@ pako@~1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258"
+parallel-transform@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06"
+ dependencies:
+ cyclist "~0.2.2"
+ inherits "^2.0.3"
+ readable-stream "^2.1.5"
+
parents@^1.0.0, parents@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parents/-/parents-1.0.1.tgz#fedd4d2bf193a77745fe71e371d73c3307d9c751"
@@ -5439,8 +6190,8 @@ parents@^1.0.0, parents@^1.0.1:
path-platform "~0.11.15"
parse-asn1@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.0.0.tgz#35060f6d5015d37628c770f4e091a0b5a278bc23"
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712"
dependencies:
asn1.js "^4.0.0"
browserify-aes "^1.0.0"
@@ -5463,6 +6214,10 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
+parse-passwd@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
+
parseqs@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.5.tgz#d5208a3738e46766e291ba2ea173684921a8b89d"
@@ -5475,14 +6230,22 @@ parseuri@0.0.5:
dependencies:
better-assert "~1.0.0"
-parseurl@~1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.1.tgz#c8ab8c9223ba34888aa64a297b28853bec18da56"
+parseurl@~1.3.1, parseurl@~1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3"
+
+pascalcase@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14"
path-browserify@0.0.0, path-browserify@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a"
+path-dirname@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
+
path-exists@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b"
@@ -5537,6 +6300,12 @@ path-type@^2.0.0:
dependencies:
pify "^2.0.0"
+path-type@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f"
+ dependencies:
+ pify "^3.0.0"
+
pause-stream@0.0.11:
version "0.0.11"
resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445"
@@ -5544,10 +6313,14 @@ pause-stream@0.0.11:
through "~2.3"
pbkdf2@^3.0.3:
- version "3.0.9"
- resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.9.tgz#f2c4b25a600058b3c3773c086c37dbbee1ffe693"
+ version "3.0.14"
+ resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade"
dependencies:
- create-hmac "^1.1.2"
+ create-hash "^1.1.2"
+ create-hmac "^1.1.4"
+ ripemd160 "^2.0.1"
+ safe-buffer "^5.0.1"
+ sha.js "^2.4.8"
performance-now@^0.2.0:
version "0.2.0"
@@ -5611,6 +6384,10 @@ portfinder@^1.0.9:
debug "^2.2.0"
mkdirp "0.5.x"
+posix-character-classes@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
+
postcss-calc@^5.2.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e"
@@ -5756,32 +6533,32 @@ postcss-minify-selectors@^2.0.4:
postcss "^5.0.14"
postcss-selector-parser "^2.0.0"
-postcss-modules-extract-imports@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.0.1.tgz#8fb3fef9a6dd0420d3f6d4353cf1ff73f2b2a341"
+postcss-modules-extract-imports@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
dependencies:
- postcss "^5.0.4"
+ postcss "^6.0.1"
-postcss-modules-local-by-default@^1.0.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.1.1.tgz#29a10673fa37d19251265ca2ba3150d9040eb4ce"
+postcss-modules-local-by-default@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069"
dependencies:
- css-selector-tokenizer "^0.6.0"
- postcss "^5.0.4"
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
-postcss-modules-scope@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.0.2.tgz#ff977395e5e06202d7362290b88b1e8cd049de29"
+postcss-modules-scope@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90"
dependencies:
- css-selector-tokenizer "^0.6.0"
- postcss "^5.0.4"
+ css-selector-tokenizer "^0.7.0"
+ postcss "^6.0.1"
-postcss-modules-values@^1.1.0:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.2.2.tgz#f0e7d476fe1ed88c5e4c7f97533a3e772ad94ca1"
+postcss-modules-values@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20"
dependencies:
- icss-replace-symbols "^1.0.2"
- postcss "^5.0.14"
+ icss-replace-symbols "^1.1.0"
+ postcss "^6.0.1"
postcss-normalize-charset@^1.1.0:
version "1.1.1"
@@ -5872,6 +6649,14 @@ postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0
source-map "^0.5.6"
supports-color "^3.2.3"
+postcss@^6.0.1:
+ version "6.0.19"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555"
+ dependencies:
+ chalk "^2.3.1"
+ source-map "^0.6.1"
+ supports-color "^5.2.0"
+
postcss@^6.0.14:
version "6.0.15"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.15.tgz#f460cd6269fede0d1bf6defff0b934a9845d974d"
@@ -5922,20 +6707,32 @@ process-nextick-args@~1.0.6:
version "1.0.7"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3"
-process@^0.11.0, process@~0.11.0:
+process-nextick-args@~2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa"
+
+process@^0.11.0:
version "0.11.9"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.9.tgz#7bd5ad21aa6253e7da8682264f1e11d11c0318c1"
+process@~0.11.0:
+ version "0.11.10"
+ resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
+
progress@^1.1.8:
version "1.1.8"
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
-proxy-addr@~1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-1.1.5.tgz#71c0ee3b102de3f202f3b64f608d173fcba1a918"
+promise-inflight@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
+
+proxy-addr@~2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341"
dependencies:
- forwarded "~0.1.0"
- ipaddr.js "1.4.0"
+ forwarded "~0.1.2"
+ ipaddr.js "1.6.0"
proxy-agent@~2.0.0:
version "2.0.0"
@@ -5954,7 +6751,7 @@ prr@~0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/prr/-/prr-0.0.0.tgz#1a84b85908325501411853d0081ee3fa86e2926a"
-ps-tree@^1.0.1:
+ps-tree@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.1.0.tgz#b421b24140d6203f1ed3c76996b4427b08e8c014"
dependencies:
@@ -5964,6 +6761,12 @@ pseudomap@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
+pstree.remy@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.0.tgz#f2af27265bd3e5b32bbfcc10e80bac55ba78688b"
+ dependencies:
+ ps-tree "^1.1.0"
+
public-encrypt@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6"
@@ -5974,6 +6777,21 @@ public-encrypt@^4.0.0:
parse-asn1 "^5.0.0"
randombytes "^2.0.1"
+pump@^2.0.0, pump@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909"
+ dependencies:
+ end-of-stream "^1.1.0"
+ once "^1.3.1"
+
+pumpify@^1.3.3:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.4.0.tgz#80b7c5df7e24153d03f0e7ac8a05a5d068bd07fb"
+ dependencies:
+ duplexify "^3.5.3"
+ inherits "^2.0.3"
+ pump "^2.0.0"
+
punycode@1.3.2:
version "1.3.2"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
@@ -5991,24 +6809,20 @@ q@~1.4.0:
resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e"
qjobs@^1.1.4:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.1.5.tgz#659de9f2cf8dcc27a1481276f205377272382e73"
-
-qs@6.4.0, qs@~6.4.0:
- version "6.4.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071"
-qs@6.5.0:
- version "6.5.0"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.0.tgz#8d04954d364def3efc55b5a0793e1e2c8b1e6e49"
+qs@6.5.1, qs@~6.5.1:
+ version "6.5.1"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
qs@~6.2.0:
version "6.2.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.2.3.tgz#1cfcb25c10a9b2b483053ff39f5dfc9233908cfe"
-qs@~6.5.1:
- version "6.5.1"
- resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8"
+qs@~6.4.0:
+ version "6.4.0"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233"
query-string@^4.1.0:
version "4.3.2"
@@ -6034,25 +6848,21 @@ querystringify@~1.0.0:
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb"
randomatic@^1.1.3:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb"
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c"
dependencies:
- is-number "^2.0.2"
- kind-of "^3.0.2"
+ is-number "^3.0.0"
+ kind-of "^4.0.0"
-randombytes@^2.0.0, randombytes@^2.0.1:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.3.tgz#674c99760901c3c4112771a31e521dc349cc09ec"
-
-randombytes@^2.0.5:
+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"
dependencies:
safe-buffer "^5.1.0"
randomfill@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.3.tgz#b96b7df587f01dd91726c418f30553b1418e3d62"
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458"
dependencies:
randombytes "^2.0.5"
safe-buffer "^5.1.0"
@@ -6071,7 +6881,7 @@ raven-js@^3.22.1:
version "3.22.1"
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.22.1.tgz#1117f00dfefaa427ef6e1a7d50bbb1fb998a24da"
-raw-body@2:
+raw-body@2, raw-body@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89"
dependencies:
@@ -6080,19 +6890,11 @@ raw-body@2:
iconv-lite "0.4.19"
unpipe "1.0.0"
-raw-body@~2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.2.0.tgz#994976cf6a5096a41162840492f0bdc5d6e7fb96"
- dependencies:
- bytes "2.4.0"
- iconv-lite "0.4.15"
- unpipe "1.0.0"
-
raw-loader@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa"
-rc@^1.0.1, rc@^1.1.7:
+rc@^1.0.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95"
dependencies:
@@ -6101,27 +6903,41 @@ rc@^1.0.1, rc@^1.1.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-react-dev-utils@^0.5.2:
- version "0.5.2"
- resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-0.5.2.tgz#50d0b962d3a94b6c2e8f2011ed6468e4124bc410"
+rc@^1.1.6, rc@^1.1.7:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd"
+ dependencies:
+ deep-extend "~0.4.0"
+ ini "~1.3.0"
+ minimist "^1.2.0"
+ strip-json-comments "~2.0.1"
+
+react-dev-utils@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-5.0.0.tgz#425ac7c9c40c2603bc4f7ab8836c1406e96bb473"
dependencies:
- ansi-html "0.0.5"
+ address "1.0.3"
+ babel-code-frame "6.26.0"
chalk "1.1.3"
+ cross-spawn "5.1.0"
+ detect-port-alt "1.1.5"
escape-string-regexp "1.0.5"
- filesize "3.3.0"
+ filesize "3.5.11"
+ global-modules "1.0.0"
gzip-size "3.0.0"
- html-entities "1.2.0"
- opn "4.0.2"
- recursive-readdir "2.1.1"
- sockjs-client "1.0.1"
+ inquirer "3.3.0"
+ is-root "1.0.0"
+ opn "5.2.0"
+ react-error-overlay "^4.0.0"
+ recursive-readdir "2.2.1"
+ shell-quote "1.6.1"
+ sockjs-client "1.1.4"
strip-ansi "3.0.1"
+ text-table "0.2.0"
-read-all-stream@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/read-all-stream/-/read-all-stream-3.1.0.tgz#35c3e177f2078ef789ee4bfafa4373074eaef4fa"
- dependencies:
- pinkie-promise "^2.0.0"
- readable-stream "^2.0.0"
+react-error-overlay@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-4.0.0.tgz#d198408a85b4070937a98667f500c832f86bd5d4"
read-only-stream@^2.0.0:
version "2.0.0"
@@ -6159,7 +6975,19 @@ read-pkg@^2.0.0:
normalize-package-data "^2.3.2"
path-type "^2.0.0"
-readable-stream@1.1.x:
+"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.3.0, readable-stream@^2.3.3:
+ version "2.3.4"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071"
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~2.0.0"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.0.3"
+ util-deprecate "~1.0.1"
+
+readable-stream@1.1.x, "readable-stream@1.x >=1.1.9":
version "1.1.14"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9"
dependencies:
@@ -6168,7 +6996,7 @@ readable-stream@1.1.x:
isarray "0.0.1"
string_decoder "~0.10.x"
-readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stream@^2.1.0, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.6, readable-stream@^2.2.9, readable-stream@^2.3.0:
+readable-stream@^2.0.0, readable-stream@^2.1.0, readable-stream@^2.2.2, readable-stream@^2.2.9:
version "2.3.3"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c"
dependencies:
@@ -6180,7 +7008,7 @@ readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.6, readable-stre
string_decoder "~1.0.3"
util-deprecate "~1.0.1"
-readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6:
+readable-stream@^2.0.1, readable-stream@^2.0.5, readable-stream@~2.0.0, readable-stream@~2.0.5, readable-stream@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.0.6.tgz#8f90341e68a53ccc928788dacfcd11b36eb9b78e"
dependencies:
@@ -6214,9 +7042,9 @@ rechoir@^0.6.2:
dependencies:
resolve "^1.1.6"
-recursive-readdir@2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.1.1.tgz#a01cfc7f7f38a53ec096a096f63a50489c3e297c"
+recursive-readdir@2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.1.tgz#90ef231d0778c5ce093c9a48d74e5c5422d13a99"
dependencies:
minimatch "3.0.3"
@@ -6274,11 +7102,17 @@ regenerator-transform@^0.10.0:
private "^0.1.6"
regex-cache@^0.4.2:
- version "0.4.3"
- resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145"
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd"
dependencies:
is-equal-shallow "^0.1.3"
- is-primitive "^2.0.0"
+
+regex-not@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
+ dependencies:
+ extend-shallow "^3.0.2"
+ safe-regex "^1.1.0"
regexpu-core@^1.0.0:
version "1.0.0"
@@ -6296,7 +7130,14 @@ regexpu-core@^2.0.0:
regjsgen "^0.2.0"
regjsparser "^0.1.4"
-registry-url@^3.0.0:
+registry-auth-token@^3.0.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.3.2.tgz#851fd49038eecb586911115af845260eec983f20"
+ dependencies:
+ rc "^1.1.6"
+ safe-buffer "^5.0.1"
+
+registry-url@^3.0.3:
version "3.1.0"
resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-3.1.0.tgz#3d4ef870f73dde1d77f0cf9a381432444e174942"
dependencies:
@@ -6324,16 +7165,10 @@ repeat-string@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae"
-repeat-string@^1.5.2:
+repeat-string@^1.5.2, repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
-repeating@^1.1.2:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac"
- dependencies:
- is-finite "^1.0.0"
-
repeating@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda"
@@ -6366,6 +7201,33 @@ request@2.75.x:
tough-cookie "~2.3.0"
tunnel-agent "~0.4.1"
+request@2.81.0:
+ version "2.81.0"
+ resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
+ dependencies:
+ aws-sign2 "~0.6.0"
+ aws4 "^1.2.1"
+ caseless "~0.12.0"
+ combined-stream "~1.0.5"
+ extend "~3.0.0"
+ forever-agent "~0.6.1"
+ form-data "~2.1.1"
+ har-validator "~4.2.1"
+ hawk "~3.1.3"
+ http-signature "~1.1.0"
+ is-typedarray "~1.0.0"
+ isstream "~0.1.2"
+ json-stringify-safe "~5.0.1"
+ mime-types "~2.1.7"
+ oauth-sign "~0.8.1"
+ performance-now "^0.2.0"
+ qs "~6.4.0"
+ safe-buffer "^5.0.1"
+ stringstream "~0.0.4"
+ tough-cookie "~2.3.0"
+ tunnel-agent "^0.6.0"
+ uuid "^3.0.0"
+
request@^2.0.0, request@^2.74.0:
version "2.83.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356"
@@ -6393,36 +7255,9 @@ request@^2.0.0, request@^2.74.0:
tunnel-agent "^0.6.0"
uuid "^3.1.0"
-request@^2.81.0:
- version "2.81.0"
- resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
- dependencies:
- aws-sign2 "~0.6.0"
- aws4 "^1.2.1"
- caseless "~0.12.0"
- combined-stream "~1.0.5"
- extend "~3.0.0"
- forever-agent "~0.6.1"
- form-data "~2.1.1"
- har-validator "~4.2.1"
- hawk "~3.1.3"
- http-signature "~1.1.0"
- is-typedarray "~1.0.0"
- isstream "~0.1.2"
- json-stringify-safe "~5.0.1"
- mime-types "~2.1.7"
- oauth-sign "~0.8.1"
- performance-now "^0.2.0"
- qs "~6.4.0"
- safe-buffer "^5.0.1"
- stringstream "~0.0.4"
- tough-cookie "~2.3.0"
- tunnel-agent "^0.6.0"
- uuid "^3.0.0"
-
requestretry@^1.2.2:
- version "1.12.2"
- resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.12.2.tgz#13ce38a4ce4e809f3c9ec6d4ca3b7b9ba4acf26c"
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/requestretry/-/requestretry-1.13.0.tgz#213ec1006eeb750e8b8ce54176283d15a8d55d94"
dependencies:
extend "^3.0.0"
lodash "^4.15.0"
@@ -6456,10 +7291,31 @@ requires-port@1.0.x, requires-port@1.x.x:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+resolve-cwd@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
+ dependencies:
+ resolve-from "^3.0.0"
+
+resolve-dir@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43"
+ dependencies:
+ expand-tilde "^2.0.0"
+ global-modules "^1.0.0"
+
resolve-from@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226"
+resolve-from@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
+
+resolve-url@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
+
resolve@1.1.7, resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
@@ -6470,12 +7326,6 @@ resolve@^1.1.3, resolve@^1.1.4, resolve@^1.1.6, resolve@^1.2.0, resolve@^1.4.0:
dependencies:
path-parse "^1.0.5"
-resolve@~1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.4.0.tgz#a75be01c53da25d934a98ebd0e4c4a7312f92a86"
- dependencies:
- path-parse "^1.0.5"
-
restore-cursor@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
@@ -6483,11 +7333,16 @@ restore-cursor@^1.0.1:
exit-hook "^1.0.0"
onetime "^1.0.0"
-resumer@~0.0.0:
- version "0.0.0"
- resolved "https://registry.yarnpkg.com/resumer/-/resumer-0.0.0.tgz#f1e8f461e4064ba39e82af3cdc2a8c893d076759"
+restore-cursor@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
dependencies:
- through "~2.3.4"
+ onetime "^2.0.0"
+ signal-exit "^3.0.2"
+
+ret@~0.1.10:
+ version "0.1.15"
+ resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
right-align@^0.1.1:
version "0.1.3"
@@ -6495,15 +7350,24 @@ right-align@^0.1.1:
dependencies:
align-text "^0.1.1"
-rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.0, rimraf@^2.6.1:
+rimraf@2, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2:
+ version "2.6.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36"
+ dependencies:
+ glob "^7.0.5"
+
+rimraf@^2.2.8:
version "2.6.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d"
dependencies:
glob "^7.0.5"
-ripemd160@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-1.0.1.tgz#93a4bbd4942bc574b69a8fa57c71de10ecca7d6e"
+ripemd160@^2.0.0, ripemd160@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
+ dependencies:
+ hash-base "^2.0.0"
+ inherits "^2.0.1"
run-async@^0.1.0:
version "0.1.0"
@@ -6511,17 +7375,41 @@ run-async@^0.1.0:
dependencies:
once "^1.3.0"
+run-async@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+ dependencies:
+ is-promise "^2.1.0"
+
+run-queue@^1.0.0, run-queue@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47"
+ dependencies:
+ aproba "^1.1.1"
+
+rx-lite-aggregates@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be"
+ dependencies:
+ rx-lite "*"
+
+rx-lite@*, rx-lite@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
+
rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
-safe-buffer@5.1.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
-safe-buffer@^5.0.1, safe-buffer@~5.0.1:
- version "5.0.1"
- resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
+safe-regex@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
+ dependencies:
+ ret "~0.1.10"
sanitize-html@^1.16.1:
version "1.16.3"
@@ -6545,6 +7433,13 @@ schema-utils@^0.3.0:
dependencies:
ajv "^5.0.0"
+schema-utils@^0.4.3, schema-utils@^0.4.5:
+ version "0.4.5"
+ resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e"
+ dependencies:
+ ajv "^6.1.0"
+ ajv-keywords "^3.1.0"
+
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -6569,32 +7464,40 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
-"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.3.0:
+"semver@2 || 3 || 4 || 5", semver@^5.0.3:
version "5.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+semver@^5.1.0, semver@^5.3.0, semver@^5.4.1:
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
+
semver@~5.0.1:
version "5.0.3"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.0.3.tgz#77466de589cd5d3c95f138aa78bc569a3cb5d27a"
-send@0.15.4:
- version "0.15.4"
- resolved "https://registry.yarnpkg.com/send/-/send-0.15.4.tgz#985faa3e284b0273c793364a35c6737bd93905b9"
+send@0.16.1:
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3"
dependencies:
- debug "2.6.8"
+ debug "2.6.9"
depd "~1.1.1"
destroy "~1.0.4"
encodeurl "~1.0.1"
escape-html "~1.0.3"
- etag "~1.8.0"
- fresh "0.5.0"
+ etag "~1.8.1"
+ fresh "0.5.2"
http-errors "~1.6.2"
- mime "1.3.4"
+ mime "1.4.1"
ms "2.0.0"
on-finished "~2.3.0"
range-parser "~1.2.0"
statuses "~1.3.1"
+serialize-javascript@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.4.0.tgz#7c958514db6ac2443a8abc062dc9f7886a7f6005"
+
serve-index@^1.7.2:
version "1.9.0"
resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.0.tgz#d2b280fc560d616ee81b48bf0fa82abed2485ce7"
@@ -6607,23 +7510,47 @@ serve-index@^1.7.2:
mime-types "~2.1.15"
parseurl "~1.3.1"
-serve-static@1.12.4:
- version "1.12.4"
- resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.12.4.tgz#9b6aa98eeb7253c4eedc4c1f6fdbca609901a961"
+serve-static@1.13.1:
+ version "1.13.1"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719"
dependencies:
encodeurl "~1.0.1"
escape-html "~1.0.3"
- parseurl "~1.3.1"
- send "0.15.4"
+ parseurl "~1.3.2"
+ send "0.16.1"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
+set-getter@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376"
+ dependencies:
+ to-object-path "^0.3.0"
+
set-immediate-shim@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61"
+set-value@^0.4.3:
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1"
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-extendable "^0.1.1"
+ is-plain-object "^2.0.1"
+ to-object-path "^0.3.0"
+
+set-value@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274"
+ dependencies:
+ extend-shallow "^2.0.1"
+ is-extendable "^0.1.1"
+ is-plain-object "^2.0.3"
+ split-string "^3.0.1"
+
setimmediate@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
@@ -6632,15 +7559,13 @@ setprototypeof@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04"
-sha.js@^2.3.6:
- version "2.4.8"
- resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.8.tgz#37068c2c476b6baf402d14a49c67f597921f634f"
- dependencies:
- inherits "^2.0.1"
+setprototypeof@1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
-sha.js@~2.4.4:
- version "2.4.9"
- resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.9.tgz#98f64880474b74f4a38b8da9d3c0f2d104633e7d"
+sha.js@^2.4.0, sha.js@^2.4.8, sha.js@~2.4.4:
+ version "2.4.10"
+ resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b"
dependencies:
inherits "^2.0.1"
safe-buffer "^5.0.1"
@@ -6662,7 +7587,7 @@ shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
-shell-quote@^1.6.1:
+shell-quote@1.6.1, shell-quote@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767"
dependencies:
@@ -6679,7 +7604,7 @@ shelljs@^0.7.5:
interpret "^1.0.0"
rechoir "^0.6.2"
-signal-exit@^3.0.0:
+signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@@ -6697,10 +7622,6 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
-slide@^1.1.5:
- version "1.1.6"
- resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707"
-
smart-buffer@^1.0.13, smart-buffer@^1.0.4:
version "1.1.15"
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16"
@@ -6712,6 +7633,33 @@ smtp-connection@2.12.0:
httpntlm "1.6.1"
nodemailer-shared "1.1.0"
+snapdragon-node@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b"
+ dependencies:
+ define-property "^1.0.0"
+ isobject "^3.0.0"
+ snapdragon-util "^3.0.1"
+
+snapdragon-util@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2"
+ dependencies:
+ kind-of "^3.2.0"
+
+snapdragon@^0.8.1:
+ version "0.8.1"
+ resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370"
+ dependencies:
+ base "^0.11.1"
+ debug "^2.2.0"
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ map-cache "^0.2.2"
+ source-map "^0.5.6"
+ source-map-resolve "^0.5.0"
+ use "^2.0.0"
+
sntp@1.x.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198"
@@ -6765,17 +7713,6 @@ socket.io@2.0.4:
socket.io-client "2.0.4"
socket.io-parser "~3.1.1"
-sockjs-client@1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.0.1.tgz#8943ae05b46547bc2054816c409002cf5e2fe026"
- dependencies:
- debug "^2.1.0"
- eventsource "^0.1.3"
- faye-websocket "~0.7.3"
- inherits "^2.0.1"
- json3 "^3.3.2"
- url-parse "^1.0.1"
-
sockjs-client@1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12"
@@ -6787,12 +7724,12 @@ sockjs-client@1.1.4:
json3 "^3.3.2"
url-parse "^1.1.8"
-sockjs@0.3.18:
- version "0.3.18"
- resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.18.tgz#d9b289316ca7df77595ef299e075f0f937eb4207"
+sockjs@0.3.19:
+ version "0.3.19"
+ resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d"
dependencies:
faye-websocket "^0.10.0"
- uuid "^2.0.2"
+ uuid "^3.0.1"
socks-proxy-agent@2:
version "2.1.1"
@@ -6822,23 +7759,33 @@ sort-keys@^1.0.0:
dependencies:
is-plain-obj "^1.0.0"
-source-list-map@^0.1.7:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-0.1.8.tgz#c550b2ab5427f6b3f21f5afead88c4f5587b2106"
-
source-list-map@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085"
+source-map-resolve@^0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a"
+ dependencies:
+ atob "^2.0.0"
+ decode-uri-component "^0.2.0"
+ resolve-url "^0.2.1"
+ 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"
dependencies:
source-map "^0.5.6"
-source-map@0.5.x, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
- version "0.5.6"
- resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+source-map-url@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3"
+
+source-map@0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86"
source-map@^0.4.4:
version "0.4.4"
@@ -6846,6 +7793,10 @@ source-map@^0.4.4:
dependencies:
amdefine ">=0.0.4"
+source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
+ version "0.5.6"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412"
+
source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@@ -6856,7 +7807,7 @@ source-map@~0.2.0:
dependencies:
amdefine ">=0.0.4"
-source-map@~0.5.6:
+source-map@~0.5.3, source-map@~0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
@@ -6897,6 +7848,12 @@ spdy@^3.4.1:
select-hose "^2.0.0"
spdy-transport "^2.0.18"
+split-string@^3.0.1, split-string@^3.0.2:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
+ dependencies:
+ extend-shallow "^3.0.0"
+
split@0.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f"
@@ -6932,7 +7889,24 @@ sshpk@^1.7.0:
jsbn "~0.1.0"
tweetnacl "~0.14.0"
-"statuses@>= 1.3.1 < 2", statuses@~1.3.1:
+ssri@^5.2.4:
+ version "5.2.4"
+ resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.2.4.tgz#9985e14041e65fc397af96542be35724ac11da52"
+ dependencies:
+ safe-buffer "^5.1.1"
+
+static-extend@^0.1.1:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
+ dependencies:
+ define-property "^0.2.5"
+ object-copy "^0.1.0"
+
+"statuses@>= 1.3.1 < 2":
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087"
+
+statuses@~1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
@@ -6956,13 +7930,20 @@ stream-combiner@~0.0.4:
dependencies:
duplexer "~0.1.1"
+stream-each@^1.1.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.2.tgz#8e8c463f91da8991778765873fe4d960d8f616bd"
+ dependencies:
+ end-of-stream "^1.1.0"
+ stream-shift "^1.0.0"
+
stream-http@^2.0.0:
- version "2.7.2"
- resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.7.2.tgz#40a050ec8dc3b53b33d9909415c02c0bf1abfbad"
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10"
dependencies:
builtin-status-codes "^3.0.0"
inherits "^2.0.1"
- readable-stream "^2.2.6"
+ readable-stream "^2.3.3"
to-arraybuffer "^1.0.0"
xtend "^4.0.0"
@@ -7000,12 +7981,6 @@ strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
-string-length@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/string-length/-/string-length-1.0.1.tgz#56970fb1c38558e9e70b728bf3de269ac45adfac"
- dependencies:
- strip-ansi "^3.0.0"
-
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@@ -7021,13 +7996,12 @@ string-width@^2.0.0:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^3.0.0"
-string.prototype.trim@~1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.1.2.tgz#d04de2c89e137f4d7d206f086b5ed2fae6be8cea"
+string-width@^2.1.0, string-width@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
dependencies:
- define-properties "^1.1.2"
- es-abstract "^1.5.0"
- function-bind "^1.0.2"
+ is-fullwidth-code-point "^2.0.0"
+ strip-ansi "^4.0.0"
string_decoder@^0.10.25, string_decoder@~0.10.x:
version "0.10.31"
@@ -7079,6 +8053,13 @@ 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"
+style-loader@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.20.2.tgz#851b373c187890331776e9cde359eea9c95ecd00"
+ dependencies:
+ loader-utils "^1.1.0"
+ schema-utils "^0.4.3"
+
subarg@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
@@ -7089,7 +8070,7 @@ supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-supports-color@^3.1.0, supports-color@^3.1.1, supports-color@^3.1.2, supports-color@^3.2.3:
+supports-color@^3.1.0, supports-color@^3.1.2, supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
@@ -7113,6 +8094,12 @@ supports-color@^5.1.0:
dependencies:
has-flag "^2.0.0"
+supports-color@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.2.0.tgz#b0d5333b1184dd3666cbe5aa0b45c5ac7ac17a4a"
+ dependencies:
+ has-flag "^3.0.0"
+
svg4everybody@2.1.9:
version "2.1.9"
resolved "https://registry.yarnpkg.com/svg4everybody/-/svg4everybody-2.1.9.tgz#5bd9f6defc133859a044646d4743fabc28db7e2d"
@@ -7130,10 +8117,10 @@ svgo@^0.7.0:
whet.extend "~0.9.9"
syntax-error@^1.1.1:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.3.0.tgz#1ed9266c4d40be75dc55bf9bb1cb77062bb96ca1"
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c"
dependencies:
- acorn "^4.0.3"
+ acorn-node "^1.2.0"
table@^3.7.8:
version "3.8.3"
@@ -7154,27 +8141,9 @@ tapable@^0.2.7:
version "0.2.8"
resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22"
-tape@^4.6.3:
- version "4.8.0"
- resolved "https://registry.yarnpkg.com/tape/-/tape-4.8.0.tgz#f6a9fec41cc50a1de50fa33603ab580991f6068e"
- dependencies:
- deep-equal "~1.0.1"
- defined "~1.0.0"
- for-each "~0.3.2"
- function-bind "~1.1.0"
- glob "~7.1.2"
- has "~1.0.1"
- inherits "~2.0.3"
- minimist "~1.2.0"
- object-inspect "~1.3.0"
- resolve "~1.4.0"
- resumer "~0.0.0"
- string.prototype.trim "~1.1.2"
- through "~2.3.8"
-
tar-pack@^3.4.0:
- version "3.4.0"
- resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984"
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f"
dependencies:
debug "^2.2.0"
fstream "^1.0.10"
@@ -7193,6 +8162,12 @@ tar@^2.2.1:
fstream "^1.0.2"
inherits "2"
+term-size@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
+ dependencies:
+ execa "^0.7.0"
+
test-exclude@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.1.1.tgz#4d84964b0966b0087ecc334a2ce002d3d9341e26"
@@ -7203,7 +8178,7 @@ test-exclude@^4.1.1:
read-pkg-up "^1.0.1"
require-main-filename "^1.0.1"
-text-table@~0.2.0:
+text-table@0.2.0, text-table@~0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -7226,7 +8201,7 @@ through2@^2.0.0:
readable-stream "^2.1.5"
xtend "~4.0.1"
-through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1, through@~2.3.4, through@~2.3.8:
+through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3, through@~2.3.1:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
@@ -7248,10 +8223,6 @@ timeago.js@^3.0.2:
dependencies:
"@types/jquery" "^2.0.40"
-timed-out@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-2.0.0.tgz#f38b0ae81d3747d628001f41dafc652ace671c0a"
-
timed-out@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
@@ -7276,18 +8247,12 @@ tiny-emitter@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.0.2.tgz#82d27468aca5ade8e5fd1e6d22b57dd43ebdfb7c"
-tmp@0.0.33:
+tmp@0.0.33, tmp@0.0.x, tmp@^0.0.33:
version "0.0.33"
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
dependencies:
os-tmpdir "~1.0.2"
-tmp@0.0.x:
- version "0.0.31"
- resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
- dependencies:
- os-tmpdir "~1.0.1"
-
to-array@0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890"
@@ -7304,19 +8269,34 @@ 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"
-touch@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/touch/-/touch-1.0.0.tgz#449cbe2dbae5a8c8038e30d71fa0ff464947c4de"
+to-object-path@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af"
dependencies:
- nopt "~1.0.10"
+ kind-of "^3.0.2"
-tough-cookie@~2.3.0:
- version "2.3.2"
- resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a"
+to-regex-range@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
dependencies:
- punycode "^1.4.1"
+ is-number "^3.0.0"
+ repeat-string "^1.6.1"
-tough-cookie@~2.3.3:
+to-regex@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.1.tgz#15358bee4a2c83bd76377ba1dc049d0f18837aae"
+ dependencies:
+ define-property "^0.2.5"
+ extend-shallow "^2.0.1"
+ regex-not "^1.0.0"
+
+touch@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/touch/-/touch-3.1.0.tgz#fe365f5f75ec9ed4e56825e0bb76d24ab74af83b"
+ dependencies:
+ nopt "~1.0.10"
+
+tough-cookie@~2.3.0, tough-cookie@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561"
dependencies:
@@ -7334,6 +8314,10 @@ trim-right@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003"
+tryer@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7"
+
tryit@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/tryit/-/tryit-1.0.3.tgz#393be730a9446fd1ead6da59a014308f36c289cb"
@@ -7342,10 +8326,14 @@ tsscmp@~1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.5.tgz#7dc4a33af71581ab4337da91d85ca5427ebd9a97"
-tty-browserify@0.0.0, tty-browserify@~0.0.0:
+tty-browserify@0.0.0:
version "0.0.0"
resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
+tty-browserify@~0.0.0:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"
+
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -7367,11 +8355,11 @@ type-check@~0.3.2:
prelude-ls "~1.1.2"
type-is@~1.6.15:
- version "1.6.15"
- resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.15.tgz#cab10fb4909e441c82842eafe1ad646c81804410"
+ version "1.6.16"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194"
dependencies:
media-typer "0.3.0"
- mime-types "~2.1.15"
+ mime-types "~2.1.18"
typedarray@^0.0.6, typedarray@~0.0.5:
version "0.0.6"
@@ -7403,8 +8391,8 @@ uid-number@^0.0.6:
resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81"
ultron@~1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.0.tgz#b07a2e6a541a815fc6a34ccd4533baec307ca864"
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
umd@^3.0.0:
version "3.0.1"
@@ -7414,9 +8402,11 @@ unc-path-regex@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
-undefsafe@0.0.3:
- version "0.0.3"
- resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-0.0.3.tgz#ecca3a03e56b9af17385baac812ac83b994a962f"
+undefsafe@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.2.tgz#225f6b9e0337663e0d8e7cfd686fc2836ccace76"
+ dependencies:
+ debug "^2.2.0"
underscore@^1.8.3:
version "1.8.3"
@@ -7426,6 +8416,15 @@ underscore@~1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
+union-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4"
+ dependencies:
+ arr-union "^3.1.0"
+ get-value "^2.0.6"
+ is-extendable "^0.1.1"
+ set-value "^0.4.3"
+
uniq@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff"
@@ -7440,28 +8439,73 @@ uniqs@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
+unique-filename@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3"
+ dependencies:
+ unique-slug "^2.0.0"
+
+unique-slug@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab"
+ dependencies:
+ imurmurhash "^0.1.4"
+
+unique-string@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-1.0.0.tgz#9e1057cca851abb93398f8b33ae187b99caec11a"
+ dependencies:
+ crypto-random-string "^1.0.0"
+
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
-update-notifier@0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-0.5.0.tgz#07b5dc2066b3627ab3b4f530130f7eddda07a4cc"
+unset-value@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559"
dependencies:
- chalk "^1.0.0"
- configstore "^1.0.0"
+ has-value "^0.3.1"
+ isobject "^3.0.0"
+
+unzip-response@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
+
+upath@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.2.tgz#80aaae5395abc5fd402933ae2f58694f0860204c"
+ dependencies:
+ lodash.endswith "^4.2.1"
+ lodash.isfunction "^3.0.8"
+ lodash.isstring "^4.0.1"
+ lodash.startswith "^4.2.1"
+
+update-notifier@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-2.3.0.tgz#4e8827a6bb915140ab093559d7014e3ebb837451"
+ dependencies:
+ boxen "^1.2.1"
+ chalk "^2.0.1"
+ configstore "^3.0.0"
+ import-lazy "^2.1.0"
+ is-installed-globally "^0.1.0"
is-npm "^1.0.0"
- latest-version "^1.0.0"
- repeating "^1.1.2"
+ latest-version "^3.0.0"
semver-diff "^2.0.0"
- string-length "^1.0.0"
+ xdg-basedir "^3.0.0"
+
+urix@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72"
-url-loader@^0.5.8:
- version "0.5.8"
- resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.5.8.tgz#b9183b1801e0f847718673673040bc9dc1c715c5"
+url-loader@^0.6.2:
+ version "0.6.2"
+ resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7"
dependencies:
loader-utils "^1.0.2"
- mime "1.3.x"
+ mime "^1.4.1"
+ schema-utils "^0.3.0"
url-parse-lax@^1.0.0:
version "1.0.0"
@@ -7476,13 +8520,6 @@ url-parse@1.0.x:
querystringify "0.0.x"
requires-port "1.0.x"
-url-parse@^1.0.1:
- version "1.1.7"
- resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.7.tgz#025cff999653a459ab34232147d89514cc87d74a"
- dependencies:
- querystringify "0.0.x"
- requires-port "1.0.x"
-
url-parse@^1.1.8:
version "1.1.9"
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.1.9.tgz#c67f1d775d51f0a18911dd7b3ffad27bb9e5bd19"
@@ -7501,6 +8538,14 @@ url@^0.11.0, url@~0.11.0:
punycode "1.3.2"
querystring "0.2.0"
+use@^2.0.0:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8"
+ dependencies:
+ define-property "^0.2.5"
+ isobject "^3.0.0"
+ lazy-cache "^2.0.2"
+
user-home@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f"
@@ -7508,10 +8553,10 @@ user-home@^2.0.0:
os-homedir "^1.0.0"
useragent@^2.1.12:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.2.1.tgz#cf593ef4f2d175875e8bb658ea92e18a4fd06d8e"
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/useragent/-/useragent-2.3.0.tgz#217f943ad540cb2128658ab23fc960f6a88c9972"
dependencies:
- lru-cache "2.2.x"
+ lru-cache "4.1.x"
tmp "0.0.x"
util-deprecate@~1.0.1:
@@ -7524,21 +8569,17 @@ util@0.10.3, util@^0.10.3, util@~0.10.1:
dependencies:
inherits "2.0.1"
-utils-merge@1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.0.tgz#0294fb922bb9375153541c4f7096231f287c8af8"
-
-uuid@^2.0.1, uuid@^2.0.2:
- version "2.0.3"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.3.tgz#67e2e863797215530dff318e5bf9dcebfd47b21a"
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
-uuid@^3.0.0, uuid@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.1.0.tgz#3dd3d3e790abc24d7b0d3a034ffababe28ebbc04"
+uuid@^3.0.0, uuid@^3.0.1, uuid@^3.1.0:
+ version "3.2.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
-uws@~0.14.4:
- version "0.14.5"
- resolved "https://registry.yarnpkg.com/uws/-/uws-0.14.5.tgz#67aaf33c46b2a587a5f6666d00f7691328f149dc"
+uws@~9.14.0:
+ version "9.14.0"
+ resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95"
validate-npm-package-license@^3.0.1:
version "3.0.1"
@@ -7551,6 +8592,10 @@ vary@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.1.tgz#67535ebb694c1d52257457984665323f587e8d37"
+vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+
vendors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22"
@@ -7592,9 +8637,9 @@ vue-hot-reload-api@^2.2.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.4.tgz#683bd1d026c0d3b3c937d5875679e9a87ec6cd8f"
-vue-loader@^13.7.0:
- version "13.7.0"
- resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.7.0.tgz#4d6a35b169c2a0a488842fb95c85052105fa9729"
+vue-loader@^14.1.1:
+ version "14.1.1"
+ resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-14.1.1.tgz#331f197fcea790d6b8662c29b850806e7eb29342"
dependencies:
consolidate "^0.14.0"
hash-sum "^1.0.2"
@@ -7607,7 +8652,7 @@ vue-loader@^13.7.0:
resolve "^1.4.0"
source-map "^0.6.1"
vue-hot-reload-api "^2.2.0"
- vue-style-loader "^3.0.0"
+ vue-style-loader "^4.0.1"
vue-template-es2015-compiler "^1.6.0"
vue-resource@^1.3.5:
@@ -7620,9 +8665,9 @@ vue-router@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
-vue-style-loader@^3.0.0:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-3.0.3.tgz#623658f81506aef9d121cdc113a4f5c9cac32df7"
+vue-style-loader@^4.0.1:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.0.2.tgz#e89aa4702a0c6b9630d8de70b1cbddb06b9ad254"
dependencies:
hash-sum "^1.0.2"
loader-utils "^1.0.2"
@@ -7660,32 +8705,24 @@ wbuf@^1.1.0, wbuf@^1.7.2:
dependencies:
minimalistic-assert "^1.0.0"
-webpack-bundle-analyzer@^2.8.2:
- version "2.8.2"
- resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.8.2.tgz#8b6240c29a9d63bc72f09d920fb050adbcce9fe8"
+webpack-bundle-analyzer@^2.10.0:
+ version "2.10.0"
+ resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.10.0.tgz#d0646cda342939f6f05eb632a090abbd90317446"
dependencies:
- acorn "^5.0.3"
- chalk "^1.1.3"
- commander "^2.9.0"
- ejs "^2.5.6"
- express "^4.15.2"
- filesize "^3.5.9"
- gzip-size "^3.0.0"
+ acorn "^5.3.0"
+ bfj-node4 "^5.2.0"
+ chalk "^2.3.0"
+ commander "^2.13.0"
+ ejs "^2.5.7"
+ express "^4.16.2"
+ filesize "^3.5.11"
+ gzip-size "^4.1.0"
lodash "^4.17.4"
mkdirp "^0.5.1"
opener "^1.4.3"
- ws "^2.3.1"
+ ws "^4.0.0"
-webpack-dev-middleware@^1.11.0:
- version "1.11.0"
- resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.11.0.tgz#09691d0973a30ad1f82ac73a12e2087f0a4754f9"
- dependencies:
- memory-fs "~0.4.1"
- mime "^1.3.4"
- path-is-absolute "^1.0.0"
- range-parser "^1.0.3"
-
-webpack-dev-middleware@^1.12.0:
+webpack-dev-middleware@1.12.2, webpack-dev-middleware@^1.12.0:
version "1.12.2"
resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e"
dependencies:
@@ -7695,33 +8732,37 @@ webpack-dev-middleware@^1.12.0:
range-parser "^1.0.3"
time-stamp "^2.0.0"
-webpack-dev-server@^2.6.1:
- version "2.7.1"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.7.1.tgz#21580f5a08cd065c71144cf6f61c345bca59a8b8"
+webpack-dev-server@^2.11.1:
+ version "2.11.1"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.1.tgz#6f9358a002db8403f016e336816f4485384e5ec0"
dependencies:
ansi-html "0.0.7"
+ array-includes "^3.0.3"
bonjour "^3.5.0"
- chokidar "^1.6.0"
+ chokidar "^2.0.0"
compression "^1.5.2"
connect-history-api-fallback "^1.3.0"
+ debug "^3.1.0"
del "^3.0.0"
- express "^4.13.3"
+ express "^4.16.2"
html-entities "^1.2.0"
http-proxy-middleware "~0.17.4"
- internal-ip "^1.2.0"
+ import-local "^1.0.0"
+ internal-ip "1.2.0"
ip "^1.1.5"
+ killable "^1.0.0"
loglevel "^1.4.1"
- opn "4.0.2"
+ opn "^5.1.0"
portfinder "^1.0.9"
selfsigned "^1.9.1"
serve-index "^1.7.2"
- sockjs "0.3.18"
+ sockjs "0.3.19"
sockjs-client "1.1.4"
spdy "^3.4.1"
strip-ansi "^3.0.0"
- supports-color "^3.1.1"
- webpack-dev-middleware "^1.11.0"
- yargs "^6.0.0"
+ supports-color "^5.1.0"
+ webpack-dev-middleware "1.12.2"
+ yargs "6.6.0"
webpack-sources@^1.0.1:
version "1.0.1"
@@ -7734,14 +8775,14 @@ webpack-stats-plugin@^0.1.5:
version "0.1.5"
resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.1.5.tgz#29e5f12ebfd53158d31d656a113ac1f7b86179d9"
-webpack@^3.5.5:
- version "3.5.5"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.5.5.tgz#3226f09fc8b3e435ff781e7af34f82b68b26996c"
+webpack@^3.11.0:
+ version "3.11.0"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.11.0.tgz#77da451b1d7b4b117adaf41a1a93b5742f24d894"
dependencies:
acorn "^5.0.0"
acorn-dynamic-import "^2.0.0"
- ajv "^5.1.5"
- ajv-keywords "^2.0.0"
+ ajv "^6.1.0"
+ ajv-keywords "^3.1.0"
async "^2.1.2"
enhanced-resolve "^3.4.0"
escope "^3.6.0"
@@ -7761,7 +8802,7 @@ webpack@^3.5.5:
webpack-sources "^1.0.1"
yargs "^8.0.2"
-websocket-driver@>=0.3.6, websocket-driver@>=0.5.1:
+websocket-driver@>=0.5.1:
version "0.6.5"
resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36"
dependencies:
@@ -7793,12 +8834,24 @@ which@^1.1.1, which@^1.2.1, which@^1.2.9:
dependencies:
isexe "^1.1.1"
+which@^1.2.14:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
+ dependencies:
+ isexe "^2.0.0"
+
wide-align@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710"
dependencies:
string-width "^1.0.2"
+widest-line@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-2.0.0.tgz#0142a4e8a243f8882c0233aa0e0281aa76152273"
+ dependencies:
+ string-width "^2.1.1"
+
window-size@0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d"
@@ -7833,13 +8886,13 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
-write-file-atomic@^1.1.2:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"
+write-file-atomic@^2.0.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
dependencies:
graceful-fs "^4.1.11"
imurmurhash "^0.1.4"
- slide "^1.1.5"
+ signal-exit "^3.0.2"
write@^0.2.1:
version "0.2.1"
@@ -7847,11 +8900,12 @@ write@^0.2.1:
dependencies:
mkdirp "^0.5.1"
-ws@^2.3.1:
- version "2.3.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-2.3.1.tgz#6b94b3e447cb6a363f785eaf94af6359e8e81c80"
+ws@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-4.0.0.tgz#bfe1da4c08eeb9780b986e0e4d10eccd7345999f"
dependencies:
- safe-buffer "~5.0.1"
+ async-limiter "~1.0.0"
+ safe-buffer "~5.1.0"
ultron "~1.1.0"
ws@~3.3.1:
@@ -7862,11 +8916,9 @@ ws@~3.3.1:
safe-buffer "~5.1.0"
ultron "~1.1.0"
-xdg-basedir@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-2.0.0.tgz#edbc903cc385fc04523d966a335504b5504d1bd2"
- dependencies:
- os-homedir "^1.0.0"
+xdg-basedir@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
xmlhttprequest-ssl@~1.5.4:
version "1.5.5"
@@ -7876,7 +8928,7 @@ xregexp@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943"
-xtend@^4.0.0, xtend@~4.0.1:
+xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
@@ -7884,6 +8936,10 @@ y18n@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41"
+y18n@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b"
+
yallist@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52"
@@ -7900,7 +8956,7 @@ yargs-parser@^7.0.0:
dependencies:
camelcase "^4.1.0"
-yargs@^6.0.0:
+yargs@6.6.0:
version "6.6.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208"
dependencies: