summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.flayignore9
-rw-r--r--.gitlab-ci.yml16
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile12
-rw-r--r--Gemfile.lock65
-rw-r--r--Gemfile.rails5.lock296
-rw-r--r--INSTALLATION_TYPE1
-rw-r--r--PROCESS.md7
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_canceled.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_created.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_failed.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_manual.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_not_found.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_pending.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_running.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_skipped.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_success.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/dev/favicon_status_warning.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_canceled.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_canceled.pngbin0 -> 864 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_created.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_created.pngbin0 -> 889 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_failed.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_failed.pngbin0 -> 1015 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_manual.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_manual.pngbin0 -> 1067 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_not_found.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_not_found.pngbin0 -> 945 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_pending.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_pending.pngbin0 -> 919 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_running.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_running.pngbin0 -> 1077 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_skipped.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_skipped.pngbin0 -> 923 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_success.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_success.pngbin0 -> 1044 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_warning.icobin4286 -> 0 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_warning.pngbin0 -> 830 bytes
-rw-r--r--app/assets/images/favicon-blue.icobin5430 -> 0 bytes
-rw-r--r--app/assets/images/favicon-blue.pngbin0 -> 1522 bytes
-rw-r--r--app/assets/images/favicon-yellow.icobin5430 -> 0 bytes
-rw-r--r--app/assets/images/favicon-yellow.pngbin0 -> 1667 bytes
-rw-r--r--app/assets/images/favicon.icobin5430 -> 0 bytes
-rw-r--r--app/assets/images/favicon.pngbin0 -> 1611 bytes
-rw-r--r--app/assets/javascripts/badges/components/badge.vue12
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue16
-rw-r--r--app/assets/javascripts/badges/components/badge_list_row.vue10
-rw-r--r--app/assets/javascripts/badges/components/badge_settings.vue2
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js36
-rw-r--r--app/assets/javascripts/boards/components/board.js50
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_card.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js5
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue53
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue18
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js57
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.js7
-rw-r--r--app/assets/javascripts/boards/components/modal/empty_state.js6
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.js6
-rw-r--r--app/assets/javascripts/boards/components/modal/header.js8
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js52
-rw-r--r--app/assets/javascripts/boards/components/modal/list.js44
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js1
-rw-r--r--app/assets/javascripts/boards/index.js2
-rw-r--r--app/assets/javascripts/boards/models/assignee.js12
-rw-r--r--app/assets/javascripts/boards/models/list.js90
-rw-r--r--app/assets/javascripts/boards/services/board_service.js10
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js24
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue8
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue18
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js2
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.vue4
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js51
-rw-r--r--app/assets/javascripts/cycle_analytics/components/banner.vue2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_component.vue2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_review_component.vue4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_test_component.vue2
-rw-r--r--app/assets/javascripts/deploy_keys/components/action_btn.vue2
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue6
-rw-r--r--app/assets/javascripts/deploy_keys/components/key.vue30
-rw-r--r--app/assets/javascripts/deploy_keys/components/keys_panel.vue2
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js5
-rw-r--r--app/assets/javascripts/diff_notes/components/diff_note_avatars.js149
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js11
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js92
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js5
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js25
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js5
-rw-r--r--app/assets/javascripts/environments/components/container.vue6
-rw-r--r--app/assets/javascripts/environments/components/environment_actions.vue14
-rw-r--r--app/assets/javascripts/environments/components/environment_external_url.vue8
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue16
-rw-r--r--app/assets/javascripts/environments/components/environment_monitoring.vue8
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue2
-rw-r--r--app/assets/javascripts/environments/components/environment_stop.vue6
-rw-r--r--app/assets/javascripts/environments/components/environment_terminal_button.vue6
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue4
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue4
-rw-r--r--app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue2
-rw-r--r--app/assets/javascripts/gl_dropdown.js6
-rw-r--r--app/assets/javascripts/group_label_subscription.js18
-rw-r--r--app/assets/javascripts/groups/components/app.vue6
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue12
-rw-r--r--app/assets/javascripts/groups/components/item_actions.vue4
-rw-r--r--app/assets/javascripts/groups/components/item_stats.vue22
-rw-r--r--app/assets/javascripts/groups/components/item_stats_value.vue2
-rw-r--r--app/assets/javascripts/ide/components/activity_bar.vue36
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/form.vue10
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list.vue12
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/list_item.vue20
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/message_field.vue10
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue8
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue12
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue6
-rw-r--r--app/assets/javascripts/ide/components/editor_mode_dropdown.vue8
-rw-r--r--app/assets/javascripts/ide/components/external_link.vue4
-rw-r--r--app/assets/javascripts/ide/components/file_finder/index.vue18
-rw-r--r--app/assets/javascripts/ide/components/file_finder/item.vue4
-rw-r--r--app/assets/javascripts/ide/components/ide.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_review.vue2
-rw-r--r--app/assets/javascripts/ide/components/ide_side_bar.vue14
-rw-r--r--app/assets/javascripts/ide/components/ide_status_bar.vue20
-rw-r--r--app/assets/javascripts/ide/components/ide_tree_list.vue4
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail.vue9
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail/description.vue4
-rw-r--r--app/assets/javascripts/ide/components/jobs/detail/scroll_button.vue4
-rw-r--r--app/assets/javascripts/ide/components/jobs/item.vue2
-rw-r--r--app/assets/javascripts/ide/components/jobs/stage.vue6
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/dropdown.vue4
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/item.vue2
-rw-r--r--app/assets/javascripts/ide/components/merge_requests/list.vue8
-rw-r--r--app/assets/javascripts/ide/components/mr_file_icon.vue4
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue8
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue6
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/upload.vue2
-rw-r--r--app/assets/javascripts/ide/components/panes/right.vue18
-rw-r--r--app/assets/javascripts/ide/components/pipelines/list.vue4
-rw-r--r--app/assets/javascripts/ide/components/repo_commit_section.vue27
-rw-r--r--app/assets/javascripts/ide/components/repo_editor.vue54
-rw-r--r--app/assets/javascripts/ide/components/repo_file.vue8
-rw-r--r--app/assets/javascripts/ide/components/repo_file_status_icon.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_loading_file.vue2
-rw-r--r--app/assets/javascripts/ide/components/repo_tab.vue8
-rw-r--r--app/assets/javascripts/ide/components/repo_tabs.vue2
-rw-r--r--app/assets/javascripts/ide/components/resizable_panel.vue4
-rw-r--r--app/assets/javascripts/ide/constants.js12
-rw-r--r--app/assets/javascripts/ide/lib/common/model.js16
-rw-r--r--app/assets/javascripts/ide/lib/common/model_manager.js5
-rw-r--r--app/assets/javascripts/ide/lib/diff/controller.js4
-rw-r--r--app/assets/javascripts/ide/lib/editor.js36
-rw-r--r--app/assets/javascripts/ide/monaco_loader.js16
-rw-r--r--app/assets/javascripts/ide/stores/modules/merge_requests/actions.js9
-rw-r--r--app/assets/javascripts/ide/stores/modules/pipelines/actions.js12
-rw-r--r--app/assets/javascripts/ide/stores/mutations/file.js14
-rw-r--r--app/assets/javascripts/ide/stores/utils.js2
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue10
-rw-r--r--app/assets/javascripts/issue_show/components/edit_actions.vue16
-rw-r--r--app/assets/javascripts/issue_show/components/edited.vue4
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description.vue8
-rw-r--r--app/assets/javascripts/issue_show/components/fields/description_template.vue10
-rw-r--r--app/assets/javascripts/issue_show/components/fields/title.vue2
-rw-r--r--app/assets/javascripts/issue_show/components/form.vue4
-rw-r--r--app/assets/javascripts/issue_show/components/title.vue4
-rw-r--r--app/assets/javascripts/jobs/components/header.vue2
-rw-r--r--app/assets/javascripts/jobs/components/sidebar_details_block.vue44
-rw-r--r--app/assets/javascripts/label_manager.js15
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js51
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js113
-rw-r--r--app/assets/javascripts/lib/utils/http_status.js1
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js4
-rw-r--r--app/assets/javascripts/main.js1
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js15
-rw-r--r--app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js13
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js9
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_service.js29
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js4
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js20
-rw-r--r--app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js32
-rw-r--r--app/assets/javascripts/merge_request_tabs.js2
-rw-r--r--app/assets/javascripts/milestone_select.js2
-rw-r--r--app/assets/javascripts/monitoring/components/empty_state.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue14
-rw-r--r--app/assets/javascripts/monitoring/components/graph/axis.vue20
-rw-r--r--app/assets/javascripts/monitoring/components/graph/deployment.vue4
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue8
-rw-r--r--app/assets/javascripts/monitoring/components/graph/legend.vue14
-rw-r--r--app/assets/javascripts/monitoring/components/graph/path.vue10
-rw-r--r--app/assets/javascripts/monitoring/components/graph/track_line.vue2
-rw-r--r--app/assets/javascripts/notebook/cells/code.vue4
-rw-r--r--app/assets/javascripts/notebook/cells/code/index.vue4
-rw-r--r--app/assets/javascripts/notebook/cells/output/index.vue2
-rw-r--r--app/assets/javascripts/notes/components/comment_form.vue28
-rw-r--r--app/assets/javascripts/notes/components/diff_file_header.vue12
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue6
-rw-r--r--app/assets/javascripts/notes/components/discussion_counter.vue6
-rw-r--r--app/assets/javascripts/notes/components/discussion_locked_widget.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue28
-rw-r--r--app/assets/javascripts/notes/components/note_awards_list.vue16
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue10
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue22
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue12
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue18
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue6
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue2
-rw-r--r--app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue6
-rw-r--r--app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue10
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue2
-rw-r--r--app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue2
-rw-r--r--app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue2
-rw-r--r--app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue22
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js14
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue8
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue32
-rw-r--r--app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue4
-rw-r--r--app/assets/javascripts/pages/sessions/new/username_validator.js9
-rw-r--r--app/assets/javascripts/pdf/index.vue4
-rw-r--r--app/assets/javascripts/pdf/page/index.vue2
-rw-r--r--app/assets/javascripts/performance_bar/components/detailed_metric.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/action_component.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/graph/job_component.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue6
-rw-r--r--app/assets/javascripts/pipelines/components/header_component.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/nav_controls.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_url.vue18
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue6
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_artifacts.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue16
-rw-r--r--app/assets/javascripts/pipelines/components/stage.vue10
-rw-r--r--app/assets/javascripts/pipelines/components/time_ago.vue8
-rw-r--r--app/assets/javascripts/profile/account/components/delete_account_modal.vue12
-rw-r--r--app/assets/javascripts/profile/account/components/update_username.vue8
-rw-r--r--app/assets/javascripts/project_label_subscription.js28
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue4
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue4
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue4
-rw-r--r--app/assets/javascripts/projects_dropdown/components/app.vue6
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue4
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_item.vue10
-rw-r--r--app/assets/javascripts/projects_dropdown/components/projects_list_search.vue2
-rw-r--r--app/assets/javascripts/projects_dropdown/components/search.vue4
-rw-r--r--app/assets/javascripts/registry/components/app.vue2
-rw-r--r--app/assets/javascripts/registry/components/collapsible_container.vue10
-rw-r--r--app/assets/javascripts/registry/components/table_registry.vue6
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.vue30
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue10
-rw-r--r--app/assets/javascripts/sidebar/components/lock/edit_form.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue10
-rw-r--r--app/assets/javascripts/sidebar/components/participants/participants.vue8
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue4
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue14
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue9
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_auto_merge_failed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue12
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue26
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue4
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_rebase.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue28
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_badge_link.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/clipboard_button.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue43
-rw-r--r--app/assets/javascripts/vue_shared/components/deprecated_modal.vue18
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/constants.js12
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue70
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/download_diff_viewer.vue69
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue160
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue158
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue41
-rw-r--r--app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue109
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/expand_button.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/file_icon.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/gl_modal.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/identicon.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/issue_warning.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_button.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_icon.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/memory_graph.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/navigation_tabs.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/panel_resizer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar/image.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/recaptcha_modal.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/table_pagination.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/tabs/tab.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/toggle_button.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue6
-rw-r--r--app/assets/javascripts/vue_shared/models/assignee.js13
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss44
-rw-r--r--app/assets/stylesheets/framework/buttons.scss12
-rw-r--r--app/assets/stylesheets/framework/common.scss8
-rw-r--r--app/assets/stylesheets/framework/files.scss48
-rw-r--r--app/assets/stylesheets/framework/forms.scss2
-rw-r--r--app/assets/stylesheets/framework/gitlab_theme.scss162
-rw-r--r--app/assets/stylesheets/framework/header.scss20
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss4
-rw-r--r--app/assets/stylesheets/framework/mixins.scss1
-rw-r--r--app/assets/stylesheets/framework/pagination.scss8
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss4
-rw-r--r--app/assets/stylesheets/framework/tables.scss89
-rw-r--r--app/assets/stylesheets/framework/timeline.scss4
-rw-r--r--app/assets/stylesheets/framework/variables.scss72
-rw-r--r--app/assets/stylesheets/pages/boards.scss11
-rw-r--r--app/assets/stylesheets/pages/builds.scss16
-rw-r--r--app/assets/stylesheets/pages/diff.scss132
-rw-r--r--app/assets/stylesheets/pages/groups.scss2
-rw-r--r--app/assets/stylesheets/pages/labels.scss227
-rw-r--r--app/assets/stylesheets/pages/members.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss7
-rw-r--r--app/assets/stylesheets/pages/note_form.scss2
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss86
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss75
-rw-r--r--app/assets/stylesheets/pages/projects.scss6
-rw-r--r--app/assets/stylesheets/pages/repo.scss7
-rw-r--r--app/assets/stylesheets/pages/search.scss5
-rw-r--r--app/assets/stylesheets/pages/settings.scss10
-rw-r--r--app/assets/stylesheets/pages/settings_ci_cd.scss9
-rw-r--r--app/assets/stylesheets/performance_bar.scss1
-rw-r--r--app/controllers/admin/appearances_controller.rb9
-rw-r--r--app/controllers/application_controller.rb10
-rw-r--r--app/controllers/boards/lists_controller.rb14
-rw-r--r--app/controllers/concerns/issuable_actions.rb30
-rw-r--r--app/controllers/concerns/issuable_collections.rb7
-rw-r--r--app/controllers/concerns/uploads_actions.rb7
-rw-r--r--app/controllers/groups/labels_controller.rb23
-rw-r--r--app/controllers/projects/clusters/applications_controller.rb2
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/registrations_controller.rb27
-rw-r--r--app/controllers/users/terms_controller.rb3
-rw-r--r--app/finders/group_members_finder.rb26
-rw-r--r--app/finders/issuable_finder.rb39
-rw-r--r--app/finders/issues_finder.rb4
-rw-r--r--app/finders/members_finder.rb4
-rw-r--r--app/finders/merge_requests_finder.rb4
-rw-r--r--app/finders/snippets_finder.rb15
-rw-r--r--app/helpers/favicon_helper.rb7
-rw-r--r--app/helpers/icons_helper.rb2
-rw-r--r--app/helpers/issuables_helper.rb2
-rw-r--r--app/helpers/labels_helper.rb8
-rw-r--r--app/helpers/page_layout_helper.rb5
-rw-r--r--app/helpers/projects_helper.rb3
-rw-r--r--app/models/appearance.rb1
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/ci/pipeline.rb21
-rw-r--r--app/models/commit_status.rb10
-rw-r--r--app/models/concerns/cache_markdown_field.rb2
-rw-r--r--app/models/concerns/enum_with_nil.rb33
-rw-r--r--app/models/group.rb29
-rw-r--r--app/models/label.rb4
-rw-r--r--app/models/list.rb18
-rw-r--r--app/models/merge_request.rb5
-rw-r--r--app/models/notification_recipient.rb31
-rw-r--r--app/models/project.rb20
-rw-r--r--app/models/project_auto_devops.rb31
-rw-r--r--app/models/project_services/chat_message/base_message.rb7
-rw-r--r--app/models/project_services/chat_message/pipeline_message.rb4
-rw-r--r--app/models/project_services/gemnasium_service.rb7
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/project_services/microsoft_teams_service.rb2
-rw-r--r--app/models/remote_mirror.rb2
-rw-r--r--app/models/timelog.rb5
-rw-r--r--app/models/user.rb5
-rw-r--r--app/serializers/status_entity.rb11
-rw-r--r--app/services/applications/create_service.rb2
-rw-r--r--app/services/boards/issues/list_service.rb21
-rw-r--r--app/services/boards/issues/move_service.rb6
-rw-r--r--app/services/boards/lists/create_service.rb20
-rw-r--r--app/services/commits/create_service.rb2
-rw-r--r--app/services/create_branch_service.rb2
-rw-r--r--app/services/delete_branch_service.rb2
-rw-r--r--app/services/merge_requests/create_from_issue_service.rb4
-rw-r--r--app/services/merge_requests/ff_merge_service.rb2
-rw-r--r--app/services/merge_requests/merge_service.rb2
-rw-r--r--app/services/projects/destroy_service.rb5
-rw-r--r--app/services/quick_actions/interpret_service.rb11
-rw-r--r--app/services/tags/create_service.rb2
-rw-r--r--app/services/tags/destroy_service.rb2
-rw-r--r--app/services/validate_new_branch_service.rb2
-rw-r--r--app/uploaders/favicon_uploader.rb24
-rw-r--r--app/uploaders/file_uploader.rb6
-rw-r--r--app/uploaders/object_storage.rb50
-rw-r--r--app/uploaders/records_uploads.rb2
-rw-r--r--app/uploaders/uploader_helper.rb2
-rw-r--r--app/validators/url_validator.rb14
-rw-r--r--app/views/admin/appearances/_form.html.haml23
-rw-r--r--app/views/admin/application_settings/_abuse.html.haml11
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml60
-rw-r--r--app/views/admin/application_settings/_background_jobs.html.haml35
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml76
-rw-r--r--app/views/admin/application_settings/_email.html.haml38
-rw-r--r--app/views/admin/application_settings/_gitaly.html.haml39
-rw-r--r--app/views/admin/application_settings/_help_page.html.haml29
-rw-r--r--app/views/admin/application_settings/_influx.html.haml104
-rw-r--r--app/views/admin/application_settings/_ip_limits.html.haml87
-rw-r--r--app/views/admin/application_settings/_koding.html.haml34
-rw-r--r--app/views/admin/application_settings/_logging.html.haml50
-rw-r--r--app/views/admin/application_settings/_outbound.html.haml11
-rw-r--r--app/views/admin/application_settings/_pages.html.haml30
-rw-r--r--app/views/admin/application_settings/_performance.html.haml25
-rw-r--r--app/views/admin/application_settings/_performance_bar.html.haml18
-rw-r--r--app/views/admin/application_settings/_plantuml.html.haml26
-rw-r--r--app/views/admin/application_settings/_prometheus.html.haml25
-rw-r--r--app/views/admin/application_settings/_realtime.html.haml23
-rw-r--r--app/views/admin/application_settings/_registry.html.haml7
-rw-r--r--app/views/admin/application_settings/_repository_check.html.haml90
-rw-r--r--app/views/admin/application_settings/_repository_mirrors_form.html.haml19
-rw-r--r--app/views/admin/application_settings/_repository_storage.html.haml89
-rw-r--r--app/views/admin/application_settings/_signin.html.haml96
-rw-r--r--app/views/admin/application_settings/_signup.html.haml94
-rw-r--r--app/views/admin/application_settings/_spam.html.haml110
-rw-r--r--app/views/admin/application_settings/_terminal.html.haml13
-rw-r--r--app/views/admin/application_settings/_terms.html.haml29
-rw-r--r--app/views/admin/application_settings/_usage.html.haml58
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml103
-rw-r--r--app/views/admin/application_settings/show.html.haml10
-rw-r--r--app/views/admin/gitaly_servers/index.html.haml4
-rw-r--r--app/views/admin/groups/_form.html.haml7
-rw-r--r--app/views/admin/groups/_group.html.haml3
-rw-r--r--app/views/admin/groups/show.html.haml8
-rw-r--r--app/views/admin/services/_form.html.haml3
-rw-r--r--app/views/award_emoji/_awards_block.html.haml4
-rw-r--r--app/views/ci/variables/_variable_row.html.haml1
-rw-r--r--app/views/devise/shared/_signup_box.html.haml7
-rw-r--r--app/views/errors/access_denied.html.haml2
-rw-r--r--app/views/groups/group_members/index.html.haml7
-rw-r--r--app/views/groups/labels/index.html.haml45
-rw-r--r--app/views/groups/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/import/gitlab_projects/new.html.haml2
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/devise.html.haml2
-rw-r--r--app/views/layouts/devise_empty.html.haml2
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml8
-rw-r--r--app/views/projects/artifacts/browse.html.haml4
-rw-r--r--app/views/projects/clusters/gcp/_form.html.haml2
-rw-r--r--app/views/projects/clusters/user/_form.html.haml12
-rw-r--r--app/views/projects/clusters/user/_show.html.haml10
-rw-r--r--app/views/projects/commit/_change.html.haml20
-rw-r--r--app/views/projects/commit/branches.html.haml2
-rw-r--r--app/views/projects/deploy_keys/_index.html.haml2
-rw-r--r--app/views/projects/edit.html.haml10
-rw-r--r--app/views/projects/empty.html.haml8
-rw-r--r--app/views/projects/labels/index.html.haml34
-rw-r--r--app/views/projects/merge_requests/show.html.haml39
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml44
-rw-r--r--app/views/projects/project_members/_groups.html.haml2
-rw-r--r--app/views/projects/project_members/_team.html.haml7
-rw-r--r--app/views/projects/protected_branches/shared/_branches_list.html.haml4
-rw-r--r--app/views/projects/protected_branches/shared/_index.html.haml2
-rw-r--r--app/views/projects/releases/edit.html.haml2
-rw-r--r--app/views/projects/services/mattermost_slash_commands/_help.html.haml27
-rw-r--r--app/views/projects/services/prometheus/_configuration_banner.html.haml2
-rw-r--r--app/views/projects/services/prometheus/_help.html.haml2
-rw-r--r--app/views/projects/services/slack_slash_commands/_help.html.haml169
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml81
-rw-r--r--app/views/projects/settings/ci_cd/_form.html.haml2
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml10
-rw-r--r--app/views/projects/tags/new.html.haml2
-rw-r--r--app/views/projects/wikis/_form.html.haml2
-rw-r--r--app/views/search/results/_blob_data.html.haml3
-rw-r--r--app/views/search/results/_commit.html.haml2
-rw-r--r--app/views/shared/_field.html.haml6
-rw-r--r--app/views/shared/_label.html.haml133
-rw-r--r--app/views/shared/_label_row.html.haml35
-rw-r--r--app/views/shared/_new_merge_request_checkbox.html.haml2
-rw-r--r--app/views/shared/_service_settings.html.haml24
-rw-r--r--app/views/shared/boards/components/_board.html.haml12
-rw-r--r--app/views/shared/empty_states/_wikis.html.haml2
-rw-r--r--app/views/shared/form_elements/_description.html.haml2
-rw-r--r--app/views/shared/issuable/_board_create_list_dropdown.html.haml8
-rw-r--r--app/views/shared/issuable/_label_page_create.html.haml3
-rw-r--r--app/views/shared/issuable/_label_page_default.html.haml7
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml9
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml6
-rw-r--r--app/views/shared/issuable/_sidebar_assignees.html.haml2
-rw-r--r--app/views/shared/issuable/form/_contribution.html.haml6
-rw-r--r--app/views/shared/members/_group.html.haml18
-rw-r--r--app/views/shared/notes/_edit_form.html.haml2
-rw-r--r--app/views/shared/notes/_form.html.haml2
-rw-r--r--app/views/shared/tokens/_scopes_form.html.haml2
-rw-r--r--app/views/users/terms/index.html.haml27
-rw-r--r--app/workers/git_garbage_collect_worker.rb58
-rw-r--r--app/workers/repository_fork_worker.rb9
-rw-r--r--app/workers/storage_migrator_worker.rb25
-rw-r--r--changelogs/unreleased/35158-snippets-api-visibility.yml5
-rw-r--r--changelogs/unreleased/38542-application-control-panel-in-settings-page.yml5
-rw-r--r--changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml5
-rw-r--r--changelogs/unreleased/42342-teams-pipeline-notifications.yml5
-rw-r--r--changelogs/unreleased/43597-new-navigation-themes.yml5
-rw-r--r--changelogs/unreleased/44267-improve-failed-jobs-tab.yml5
-rw-r--r--changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml5
-rw-r--r--changelogs/unreleased/44790-disabled-emails-logging.yml5
-rw-r--r--changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml5
-rw-r--r--changelogs/unreleased/45505-lograge_formatter_encoding.yml6
-rw-r--r--changelogs/unreleased/45575-invalid-characters-signup.yml5
-rw-r--r--changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml5
-rw-r--r--changelogs/unreleased/46648-timeout-searching-group-issues.yml5
-rw-r--r--changelogs/unreleased/46922-hashed-storage-single-project.yml5
-rw-r--r--changelogs/unreleased/47050-quick-actions-case-insensitive.yml5
-rw-r--r--changelogs/unreleased/47145-quick-actions-confidential.yml5
-rw-r--r--changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml5
-rw-r--r--changelogs/unreleased/47208-human-import-status-name-not-working.yml5
-rw-r--r--changelogs/unreleased/47408-migrateuploadsworker-is-doing-n-1-queries-on-migration.yml5
-rw-r--r--changelogs/unreleased/47513-upload-migration-lease-key-is-incorrect-for-non-mounted-uploaders.yml5
-rw-r--r--changelogs/unreleased/47604-avatars-and-system-icons-for-mobile.yml5
-rw-r--r--changelogs/unreleased/47679-fix-failed-jobs-tab-ie11.yml5
-rw-r--r--changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml5
-rw-r--r--changelogs/unreleased/author-doc-fix.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml5
-rw-r--r--changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml6
-rw-r--r--changelogs/unreleased/bvl-terms-on-registration.yml5
-rw-r--r--changelogs/unreleased/bw-enable-commonmark.yml5
-rw-r--r--changelogs/unreleased/cache-doc-fix.yml5
-rw-r--r--changelogs/unreleased/commits_api_with_stats.yml5
-rw-r--r--changelogs/unreleased/da-port-cte-to-ce.yml5
-rw-r--r--changelogs/unreleased/expose-ci-url.yml5
-rw-r--r--changelogs/unreleased/feature-customizable-favicon.yml5
-rw-r--r--changelogs/unreleased/fix-br-decode.yml5
-rw-r--r--changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml5
-rw-r--r--changelogs/unreleased/fj-relax-url-validator-rules-for-user.yml5
-rw-r--r--changelogs/unreleased/fj-restore-users-v3-endpoint.yml5
-rw-r--r--changelogs/unreleased/gitaly-commit-count-opt-out.yml5
-rw-r--r--changelogs/unreleased/gitaly-opt-out-branch-tag.yml5
-rw-r--r--changelogs/unreleased/ide-url-util-relative-url-fix.yml6
-rw-r--r--changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml5
-rw-r--r--changelogs/unreleased/issue_44230.yml5
-rw-r--r--changelogs/unreleased/mk-rake-task-verify-remote-files.yml5
-rw-r--r--changelogs/unreleased/n-plus-one-notification-recipients.yml5
-rw-r--r--changelogs/unreleased/pr-importer-io-extra-error-handling.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47366.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47370.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47804.yml5
-rw-r--r--changelogs/unreleased/rails5-fix-47805.yml6
-rw-r--r--changelogs/unreleased/rails5-fix-47835.yml6
-rw-r--r--changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml5
-rw-r--r--changelogs/unreleased/rosulk-patch-12.yml5
-rw-r--r--changelogs/unreleased/sh-expire-content-cache-after-import.yml5
-rw-r--r--changelogs/unreleased/sh-log-422-responses.yml6
-rw-r--r--changelogs/unreleased/sql-buckets.yml5
-rw-r--r--changelogs/unreleased/tz-diff-blob-image-viewer.yml5
-rw-r--r--changelogs/unreleased/unify-views-search-results.yml5
-rw-r--r--changelogs/unreleased/upgrade-gitlab-markup.yml5
-rw-r--r--changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml5
-rw-r--r--changelogs/unreleased/winh-new-branch-url-encode.yml5
-rw-r--r--config/application.rb5
-rw-r--r--config/dependency_decisions.yml6
-rw-r--r--config/environments/test.rb4
-rw-r--r--config/gitlab.yml.example11
-rw-r--r--config/initializers/disable_email_interceptor.rb5
-rw-r--r--config/initializers/lograge.rb1
-rw-r--r--config/initializers/mime_types.rb2
-rw-r--r--config/initializers/mini_magick.rb3
-rw-r--r--config/initializers/postgresql_opclasses_support.rb7
-rw-r--r--config/initializers/rack_attack_global.rb6
-rw-r--r--config/locales/carrierwave.en.yml14
-rw-r--r--config/prometheus/additional_metrics.yml8
-rw-r--r--config/routes/admin.rb1
-rw-r--r--config/routes/uploads.rb2
-rw-r--r--config/routes/wiki.rb2
-rw-r--r--config/unicorn.rb.example11
-rw-r--r--config/webpack.config.js37
-rw-r--r--db/fixtures/development/04_project.rb4
-rw-r--r--db/fixtures/development/08_settings.rb7
-rw-r--r--db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb4
-rw-r--r--db/migrate/20161226122833_remove_dot_git_from_usernames.rb4
-rw-r--r--db/migrate/20170925184228_add_favicon_to_appearances.rb7
-rw-r--r--db/migrate/20180523042841_rename_merge_requests_allow_maintainer_to_push.rb6
-rw-r--r--db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb19
-rw-r--r--db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb21
-rw-r--r--db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb6
-rw-r--r--db/post_migrate/20180608201435_cleanup_merge_requests_allow_collaboration_rename.rb20
-rw-r--r--db/schema.rb6
-rw-r--r--doc/administration/index.md1
-rw-r--r--doc/administration/integration/koding.md18
-rw-r--r--doc/administration/raketasks/check.md7
-rw-r--r--doc/administration/raketasks/storage.md45
-rw-r--r--doc/api/commits.md1
-rw-r--r--doc/api/merge_requests.md62
-rw-r--r--doc/api/snippets.md6
-rw-r--r--doc/ci/environments.md4
-rw-r--r--doc/ci/examples/README.md4
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.pngbin0 -> 49735 bytes
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.pngbin0 -> 82121 bytes
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md142
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/ci/yaml/README.md10
-rw-r--r--doc/customization/favicon.md16
-rw-r--r--doc/customization/favicon/appearance.pngbin0 -> 52245 bytes
-rw-r--r--doc/customization/favicon/custom_favicon.pngbin0 -> 60083 bytes
-rw-r--r--doc/development/README.md2
-rw-r--r--doc/development/changelog.md2
-rw-r--r--doc/development/fe_guide/style_guide_scss.md10
-rw-r--r--doc/development/i18n/externalization.md23
-rw-r--r--doc/development/new_fe_guide/development/testing.md136
-rw-r--r--doc/gitlab-basics/command-line-commands.md58
-rw-r--r--doc/gitlab-basics/start-using-git.md147
-rw-r--r--doc/install/installation.md4
-rw-r--r--doc/install/kubernetes/gitlab_omnibus.md2
-rw-r--r--doc/install/openshift_and_gitlab/index.md8
-rw-r--r--doc/topics/autodevops/img/autodevops_domain_variables.pngbin0 -> 8456 bytes
-rw-r--r--doc/topics/autodevops/img/autodevops_multiple_clusters.pngbin0 -> 12619 bytes
-rw-r--r--doc/topics/autodevops/index.md176
-rw-r--r--doc/university/README.md12
-rw-r--r--doc/university/glossary/README.md4
-rw-r--r--doc/update/10.8-to-11.0.md362
-rw-r--r--doc/user/admin_area/settings/img/enforce_terms.pngbin51979 -> 54881 bytes
-rw-r--r--doc/user/admin_area/settings/img/sign_up_terms.pngbin0 -> 18174 bytes
-rw-r--r--doc/user/admin_area/settings/terms.md13
-rw-r--r--doc/user/gitlab_com/index.md1
-rw-r--r--doc/user/index.md6
-rw-r--r--doc/user/markdown.md163
-rw-r--r--doc/user/profile/img/profil-preferences-navigation-theme.pngbin0 -> 16403 bytes
-rw-r--r--doc/user/profile/img/profile-preferences-syntax-themes.pngbin0 -> 44844 bytes
-rw-r--r--doc/user/profile/img/profile_settings_dropdown.pngbin4184 -> 6972 bytes
-rw-r--r--doc/user/profile/preferences.md42
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md13
-rw-r--r--doc/user/project/clusters/index.md18
-rw-r--r--doc/user/project/import/github.md4
-rw-r--r--doc/user/project/import/img/import_projects_from_repo_url.pngbin150259 -> 142329 bytes
-rw-r--r--doc/user/project/integrations/prometheus.md2
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress.md2
-rw-r--r--doc/user/project/issue_board.md16
-rw-r--r--doc/user/project/quick_actions.md1
-rw-r--r--doc/user/project/web_ide/index.md21
-rw-r--r--doc/user/reserved_names.md2
-rw-r--r--doc/workflow/notifications.md9
-rw-r--r--doc/workflow/repository_mirroring.md2
-rw-r--r--lib/api/commits.rb22
-rw-r--r--lib/api/entities.rb10
-rw-r--r--lib/api/helpers.rb3
-rw-r--r--lib/api/helpers/runner.rb5
-rw-r--r--lib/api/markdown.rb4
-rw-r--r--lib/api/runner.rb6
-rw-r--r--lib/api/users.rb26
-rw-r--r--lib/backup/files.rb11
-rw-r--r--lib/backup/repository.rb33
-rw-r--r--lib/banzai/filter/markdown_filter.rb2
-rw-r--r--lib/banzai/filter/milestone_reference_filter.rb2
-rw-r--r--lib/gitlab.rb1
-rw-r--r--lib/gitlab/current_settings.rb15
-rw-r--r--lib/gitlab/cycle_analytics/summary/commit.rb20
-rw-r--r--lib/gitlab/favicon.rb47
-rw-r--r--lib/gitlab/git.rb2
-rw-r--r--lib/gitlab/git/blame.rb19
-rw-r--r--lib/gitlab/git/committer_with_hooks.rb2
-rw-r--r--lib/gitlab/git/gitlab_projects.rb37
-rw-r--r--lib/gitlab/git/hook.rb6
-rw-r--r--lib/gitlab/git/hooks_service.rb2
-rw-r--r--lib/gitlab/git/lfs_changes.rb26
-rw-r--r--lib/gitlab/git/pre_receive_error.rb21
-rw-r--r--lib/gitlab/git/repository.rb177
-rw-r--r--lib/gitlab/git/rev_list.rb7
-rw-r--r--lib/gitlab/git/version.rb11
-rw-r--r--lib/gitlab/gitaly_client.rb14
-rw-r--r--lib/gitlab/gitaly_client/commit_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb16
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb55
-rw-r--r--lib/gitlab/github_import/importer/pull_request_importer.rb3
-rw-r--r--lib/gitlab/github_import/parallel_importer.rb9
-rw-r--r--lib/gitlab/github_import/sequential_importer.rb2
-rw-r--r--lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb17
-rw-r--r--lib/gitlab/hashed_storage/migrator.rb57
-rw-r--r--lib/gitlab/hashed_storage/rake_helper.rb14
-rw-r--r--lib/gitlab/metrics/subscribers/active_record.rb2
-rw-r--r--lib/gitlab/metrics/transaction.rb2
-rw-r--r--lib/gitlab/path_regex.rb2
-rw-r--r--lib/gitlab/quick_actions/extractor.rb8
-rw-r--r--lib/gitlab/quick_actions/substitution_definition.rb2
-rw-r--r--lib/gitlab/setup_helper.rb4
-rw-r--r--lib/gitlab/shell.rb59
-rw-r--r--lib/gitlab/sql/cte.rb50
-rw-r--r--lib/gitlab/task_helpers.rb4
-rw-r--r--lib/gitlab/themes.rb15
-rw-r--r--lib/gitlab/url_blocker.rb4
-rw-r--r--lib/gitlab/url_builder.rb2
-rw-r--r--lib/gitlab/usage_data.rb1
-rw-r--r--lib/gitlab/verify/batch_verifier.rb59
-rw-r--r--lib/gitlab/verify/job_artifacts.rb10
-rw-r--r--lib/gitlab/verify/lfs_objects.rb12
-rw-r--r--lib/gitlab/verify/rake_task.rb2
-rw-r--r--lib/gitlab/verify/uploads.rb12
-rw-r--r--lib/microsoft_teams/notifier.rb2
-rw-r--r--lib/system_check/orphans/namespace_check.rb16
-rw-r--r--lib/system_check/orphans/repository_check.rb16
-rw-r--r--lib/system_check/simple_executor.rb30
-rw-r--r--lib/tasks/gitlab/check.rake12
-rw-r--r--lib/tasks/gitlab/info.rake6
-rw-r--r--lib/tasks/gitlab/storage.rake21
-rw-r--r--lib/tasks/import.rake7
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake15
-rw-r--r--locale/gitlab.pot248
-rw-r--r--package.json10
-rw-r--r--public/favicon.icobin5430 -> 0 bytes
-rw-r--r--public/favicon.pngbin0 -> 1611 bytes
-rw-r--r--qa/qa.rb6
-rw-r--r--qa/qa/factory/repository/push.rb10
-rw-r--r--qa/qa/factory/resource/branch.rb18
-rw-r--r--qa/qa/git/repository.rb19
-rw-r--r--qa/qa/page/admin/settings/main.rb4
-rw-r--r--qa/qa/page/base.rb2
-rw-r--r--qa/qa/page/group/show.rb6
-rw-r--r--qa/qa/page/project/job/show.rb5
-rw-r--r--qa/qa/page/project/pipeline/show.rb4
-rw-r--r--qa/qa/page/project/settings/advanced.rb6
-rw-r--r--qa/qa/page/project/settings/ci_cd.rb18
-rw-r--r--qa/qa/page/project/settings/main.rb4
-rw-r--r--qa/qa/page/project/settings/merge_request.rb12
-rw-r--r--qa/qa/page/project/settings/protected_branches.rb8
-rw-r--r--qa/qa/page/project/settings/repository.rb10
-rw-r--r--qa/qa/page/settings/common.rb20
-rw-r--r--qa/qa/runtime/api.rb82
-rw-r--r--qa/qa/runtime/api/client.rb39
-rw-r--r--qa/qa/runtime/api/request.rb43
-rw-r--r--qa/qa/runtime/browser.rb14
-rw-r--r--qa/qa/runtime/env.rb12
-rw-r--r--qa/qa/service/kubernetes_cluster.rb17
-rw-r--r--qa/qa/specs/features/api/basics_spec.rb61
-rw-r--r--qa/qa/specs/features/api/users_spec.rb2
-rw-r--r--qa/qa/specs/features/merge_request/create_spec.rb2
-rw-r--r--qa/qa/specs/features/project/auto_devops_spec.rb2
-rw-r--r--qa/qa/specs/features/project/deploy_key_clone_spec.rb4
-rw-r--r--qa/qa/specs/features/repository/protected_branches_spec.rb67
-rw-r--r--qa/spec/git/repository_spec.rb40
-rw-r--r--qa/spec/runtime/api/client_spec.rb (renamed from qa/spec/runtime/api_client_spec.rb)0
-rw-r--r--qa/spec/runtime/api/request_spec.rb44
-rw-r--r--qa/spec/runtime/api_request_spec.rb42
-rw-r--r--qa/spec/support/stub_env.rb2
-rwxr-xr-xscripts/rails5-gemfile-lock-check19
-rw-r--r--spec/controllers/admin/application_settings_controller_spec.rb2
-rw-r--r--spec/controllers/application_controller_spec.rb68
-rw-r--r--spec/controllers/boards/issues_controller_spec.rb2
-rw-r--r--spec/controllers/boards/lists_controller_spec.rb18
-rw-r--r--spec/controllers/groups/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/import/gitlab_projects_controller_spec.rb2
-rw-r--r--spec/controllers/import/google_code_controller_spec.rb2
-rw-r--r--spec/controllers/profiles/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/projects/avatars_controller_spec.rb2
-rw-r--r--spec/controllers/projects/jobs_controller_spec.rb2
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb2
-rw-r--r--spec/controllers/projects/pipelines_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb16
-rw-r--r--spec/controllers/registrations_controller_spec.rb21
-rw-r--r--spec/controllers/uploads_controller_spec.rb118
-rw-r--r--spec/factories/lfs_objects.rb2
-rw-r--r--spec/factories/notes.rb4
-rw-r--r--spec/factories/project_auto_devops.rb9
-rw-r--r--spec/factories/projects.rb12
-rw-r--r--spec/features/admin/admin_appearance_spec.rb20
-rw-r--r--spec/features/admin/admin_settings_spec.rb2
-rw-r--r--spec/features/admin/admin_uses_repository_checks_spec.rb2
-rw-r--r--spec/features/boards/boards_spec.rb2
-rw-r--r--spec/features/commits_spec.rb2
-rw-r--r--spec/features/issues/form_spec.rb3
-rw-r--r--spec/features/issues/user_uses_slash_commands_spec.rb36
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb9
-rw-r--r--spec/features/markdown/markdown_spec.rb55
-rw-r--r--spec/features/merge_request/user_creates_image_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb4
-rw-r--r--spec/features/projects/issues/user_comments_on_issue_spec.rb7
-rw-r--r--spec/features/projects/jobs/permissions_spec.rb3
-rw-r--r--spec/features/projects/jobs_spec.rb2
-rw-r--r--spec/features/projects/labels/subscription_spec.rb6
-rw-r--r--spec/features/projects/labels/update_prioritization_spec.rb8
-rw-r--r--spec/features/projects/labels/user_removes_labels_spec.rb20
-rw-r--r--spec/features/projects/pages_spec.rb4
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb10
-rw-r--r--spec/features/tags/master_deletes_tag_spec.rb4
-rw-r--r--spec/features/task_lists_spec.rb45
-rw-r--r--spec/features/users/signup_spec.rb21
-rw-r--r--spec/features/users/terms_spec.rb143
-rw-r--r--spec/finders/group_members_finder_spec.rb12
-rw-r--r--spec/finders/members_finder_spec.rb12
-rw-r--r--spec/fixtures/api/schemas/list.json2
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json14
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json4
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/milestones.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/snippets.json1
-rw-r--r--spec/fixtures/markdown.md.erb10
-rw-r--r--spec/helpers/emails_helper_spec.rb4
-rw-r--r--spec/helpers/groups_helper_spec.rb8
-rw-r--r--spec/helpers/markup_helper_spec.rb2
-rw-r--r--spec/helpers/page_layout_helper_spec.rb17
-rw-r--r--spec/helpers/preferences_helper_spec.rb4
-rw-r--r--spec/helpers/projects_helper_spec.rb10
-rw-r--r--spec/helpers/storage_helper_spec.rb24
-rw-r--r--spec/javascripts/boards/board_card_spec.js2
-rw-r--r--spec/javascripts/boards/boards_store_spec.js2
-rw-r--r--spec/javascripts/boards/issue_card_spec.js2
-rw-r--r--spec/javascripts/boards/issue_spec.js2
-rw-r--r--spec/javascripts/boards/list_spec.js2
-rw-r--r--spec/javascripts/boards/modal_store_spec.js2
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js2
-rw-r--r--spec/javascripts/create_merge_request_dropdown_spec.js67
-rw-r--r--spec/javascripts/datetime_utility_spec.js23
-rw-r--r--spec/javascripts/fixtures/images/green_box.pngbin0 -> 1306 bytes
-rw-r--r--spec/javascripts/fixtures/images/red_box.pngbin0 -> 1305 bytes
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_item_spec.js17
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/list_spec.js2
-rw-r--r--spec/javascripts/ide/components/jobs/detail_spec.js5
-rw-r--r--spec/javascripts/ide/components/pipelines/list_spec.js11
-rw-r--r--spec/javascripts/ide/components/repo_commit_section_spec.js14
-rw-r--r--spec/javascripts/ide/components/repo_editor_spec.js6
-rw-r--r--spec/javascripts/ide/lib/common/model_manager_spec.js10
-rw-r--r--spec/javascripts/ide/lib/common/model_spec.js18
-rw-r--r--spec/javascripts/ide/lib/decorations/controller_spec.js18
-rw-r--r--spec/javascripts/ide/lib/diff/controller_spec.js25
-rw-r--r--spec/javascripts/ide/lib/editor_spec.js25
-rw-r--r--spec/javascripts/ide/monaco_loader_spec.js15
-rw-r--r--spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js47
-rw-r--r--spec/javascripts/ide/stores/mutations/file_spec.js47
-rw-r--r--spec/javascripts/jobs/mock_data.js4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js47
-rw-r--r--spec/javascripts/lib/utils/mock_data.js5
-rw-r--r--spec/javascripts/lib/utils/url_utility_spec.js29
-rw-r--r--spec/javascripts/notes/components/comment_form_spec.js2
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js4
-rw-r--r--spec/javascripts/notes/components/note_form_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/mock_data.js18
-rw-r--r--spec/javascripts/shortcuts_issuable_spec.js2
-rw-r--r--spec/javascripts/test_constants.js3
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_author_time_spec.js8
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js12
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js23
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/mr_widget_options_spec.js13
-rw-r--r--spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js10
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js70
-rw-r--r--spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js185
-rw-r--r--spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js13
-rw-r--r--spec/lib/banzai/filter/image_lazy_load_filter_spec.rb2
-rw-r--r--spec/lib/banzai/filter/markdown_filter_spec.rb4
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb10
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb143
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb4
-rw-r--r--spec/lib/gitlab/favicon_spec.rb52
-rw-r--r--spec/lib/gitlab/git/blame_spec.rb10
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb14
-rw-r--r--spec/lib/gitlab/git/branch_spec.rb4
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb27
-rw-r--r--spec/lib/gitlab/git/diff_spec.rb6
-rw-r--r--spec/lib/gitlab/git/gitlab_projects_spec.rb112
-rw-r--r--spec/lib/gitlab/git/hook_spec.rb32
-rw-r--r--spec/lib/gitlab/git/hooks_service_spec.rb8
-rw-r--r--spec/lib/gitlab/git/index_spec.rb7
-rw-r--r--spec/lib/gitlab/git/pre_receive_error_spec.rb9
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb79
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb2
-rw-r--r--spec/lib/gitlab/git/wiki_spec.rb16
-rw-r--r--spec/lib/gitlab/git_access_wiki_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb12
-rw-r--r--spec/lib/gitlab/github_import/sequential_importer_spec.rb1
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb75
-rw-r--r--spec/lib/gitlab/import_export/avatar_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/import_export/merge_request_parser_spec.rb4
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml3
-rw-r--r--spec/lib/gitlab/import_export/uploads_saver_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/extractor_spec.rb16
-rw-r--r--spec/lib/gitlab/shell_spec.rb72
-rw-r--r--spec/lib/gitlab/sql/cte_spec.rb42
-rw-r--r--spec/lib/gitlab/themes_spec.rb6
-rw-r--r--spec/lib/gitlab/url_blocker_spec.rb46
-rw-r--r--spec/lib/gitlab/url_builder_spec.rb25
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb2
-rw-r--r--spec/lib/gitlab/verify/job_artifacts_spec.rb29
-rw-r--r--spec/lib/gitlab/verify/lfs_objects_spec.rb25
-rw-r--r--spec/lib/gitlab/verify/uploads_spec.rb27
-rw-r--r--spec/lib/microsoft_teams/notifier_spec.rb2
-rw-r--r--spec/migrations/active_record/schema_spec.rb6
-rw-r--r--spec/migrations/migrate_process_commit_worker_jobs_spec.rb6
-rw-r--r--spec/migrations/turn_nested_groups_into_regular_groups_for_mysql_spec.rb8
-rw-r--r--spec/models/ci/build_spec.rb3
-rw-r--r--spec/models/ci/job_artifact_spec.rb4
-rw-r--r--spec/models/ci/pipeline_spec.rb2
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb10
-rw-r--r--spec/models/group_spec.rb26
-rw-r--r--spec/models/notification_recipient_spec.rb44
-rw-r--r--spec/models/project_auto_devops_spec.rb121
-rw-r--r--spec/models/project_services/jira_service_spec.rb19
-rw-r--r--spec/models/project_services/microsoft_teams_service_spec.rb7
-rw-r--r--spec/models/project_spec.rb66
-rw-r--r--spec/models/project_wiki_spec.rb6
-rw-r--r--spec/models/remote_mirror_spec.rb11
-rw-r--r--spec/models/repository_spec.rb10
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/requests/api/commits_spec.rb43
-rw-r--r--spec/requests/api/internal_spec.rb1
-rw-r--r--spec/requests/api/project_import_spec.rb2
-rw-r--r--spec/requests/api/projects_spec.rb4
-rw-r--r--spec/requests/api/runner_spec.rb34
-rw-r--r--spec/requests/api/snippets_spec.rb3
-rw-r--r--spec/requests/api/users_spec.rb75
-rw-r--r--spec/requests/git_http_spec.rb17
-rw-r--r--spec/requests/openid_connect_spec.rb2
-rw-r--r--spec/routing/project_routing_spec.rb20
-rw-r--r--spec/serializers/build_serializer_spec.rb4
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb2
-rw-r--r--spec/serializers/status_entity_spec.rb12
-rw-r--r--spec/services/git_tag_push_service_spec.rb4
-rw-r--r--spec/services/merge_requests/create_from_issue_service_spec.rb11
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/squash_service_spec.rb4
-rw-r--r--spec/services/notification_recipient_service_spec.rb36
-rw-r--r--spec/services/notification_service_spec.rb188
-rw-r--r--spec/services/projects/after_import_service_spec.rb8
-rw-r--r--spec/services/projects/create_service_spec.rb5
-rw-r--r--spec/services/projects/destroy_service_spec.rb6
-rw-r--r--spec/services/projects/fork_service_spec.rb2
-rw-r--r--spec/services/projects/gitlab_projects_import_service_spec.rb2
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb6
-rw-r--r--spec/services/projects/housekeeping_service_spec.rb72
-rw-r--r--spec/services/projects/participants_service_spec.rb2
-rw-r--r--spec/services/projects/update_pages_service_spec.rb12
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb18
-rw-r--r--spec/services/system_note_service_spec.rb9
-rw-r--r--spec/services/tags/create_service_spec.rb2
-rw-r--r--spec/services/upload_service_spec.rb10
-rw-r--r--spec/spec_helper.rb13
-rw-r--r--spec/support/api/scopes/read_user_shared_examples.rb10
-rw-r--r--spec/support/features/reportable_note_shared_examples.rb4
-rw-r--r--spec/support/gitaly.rb2
-rw-r--r--spec/support/helpers/assets_helpers.rb15
-rw-r--r--spec/support/helpers/test_env.rb10
-rw-r--r--spec/support/rspec.rb2
-rw-r--r--spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb4
-rw-r--r--spec/support/shared_examples/uploaders/object_storage_shared_examples.rb10
-rw-r--r--spec/support/shoulda/matchers/rails_shim.rb27
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb4
-rw-r--r--spec/tasks/gitlab/shell_rake_spec.rb8
-rw-r--r--spec/tasks/gitlab/storage_rake_spec.rb45
-rw-r--r--spec/uploaders/attachment_uploader_spec.rb2
-rw-r--r--spec/uploaders/favicon_uploader_spec.rb29
-rw-r--r--spec/uploaders/file_mover_spec.rb2
-rw-r--r--spec/uploaders/file_uploader_spec.rb2
-rw-r--r--spec/uploaders/gitlab_uploader_spec.rb2
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb8
-rw-r--r--spec/uploaders/legacy_artifact_uploader_spec.rb3
-rw-r--r--spec/uploaders/namespace_file_uploader_spec.rb2
-rw-r--r--spec/uploaders/object_storage_spec.rb18
-rw-r--r--spec/uploaders/personal_file_uploader_spec.rb2
-rw-r--r--spec/uploaders/records_uploads_spec.rb2
-rw-r--r--spec/uploaders/uploader_helper_spec.rb2
-rw-r--r--spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb51
-rw-r--r--spec/validators/url_validator_spec.rb53
-rw-r--r--spec/views/errors/access_denied.html.haml_spec.rb7
-rw-r--r--spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb8
-rw-r--r--spec/workers/git_garbage_collect_worker_spec.rb125
-rw-r--r--spec/workers/project_destroy_worker_spec.rb6
-rw-r--r--spec/workers/repository_fork_worker_spec.rb14
-rw-r--r--spec/workers/repository_import_worker_spec.rb15
-rw-r--r--spec/workers/repository_remove_remote_worker_spec.rb4
-rw-r--r--spec/workers/storage_migrator_worker_spec.rb25
-rw-r--r--vendor/gitignore/Dart.gitignore10
-rw-r--r--vendor/gitignore/Global/JetBrains.gitignore1
-rw-r--r--vendor/gitignore/Node.gitignore6
-rw-r--r--vendor/gitignore/Sass.gitignore2
-rw-r--r--vendor/gitignore/TeX.gitignore4
-rw-r--r--vendor/gitignore/Terraform.gitignore12
-rw-r--r--vendor/gitignore/VisualStudio.gitignore1
-rw-r--r--vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml29
-rw-r--r--vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml87
-rw-r--r--vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml74
-rw-r--r--vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml74
-rw-r--r--vendor/licenses.csv389
-rw-r--r--yarn.lock1155
1019 files changed, 11651 insertions, 6804 deletions
diff --git a/.flayignore b/.flayignore
index 7faa6c7bb90..3e5063674ff 100644
--- a/.flayignore
+++ b/.flayignore
@@ -14,3 +14,12 @@ lib/gitlab/gitaly_client/ref_service.rb
lib/gitlab/gitaly_client/commit_service.rb
lib/gitlab/git/commit.rb
lib/gitlab/git/tag.rb
+
+ee/db/**/*
+ee/app/serializers/ee/merge_request_widget_entity.rb
+ee/lib/api/epics.rb
+ee/lib/api/geo_nodes.rb
+ee/lib/ee/gitlab/ldap/sync/admin_users.rb
+ee/app/workers/geo/file_download_dispatch_worker/job_artifact_job_finder.rb
+ee/app/workers/geo/file_download_dispatch_worker/lfs_object_job_finder.rb
+ee/spec/**/*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1679ae378c9..9a0102c65fd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,4 +1,4 @@
-image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-65.0-node-8.x-yarn-1.2-postgresql-9.6"
+image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.4.4-golang-1.9-git-2.17-chrome-67.0-node-8.x-yarn-1.2-postgresql-9.6-graphicsmagick-1.3.29"
.dedicated-runner: &dedicated-runner
retry: 1
@@ -394,7 +394,11 @@ compile-assets:
- date
- yarn install --frozen-lockfile --cache-folder .yarn-cache
- date
+ - free -m
- bundle exec rake gitlab:assets:compile
+ variables:
+ # we override the max_old_space_size to prevent OOM errors
+ NODE_OPTIONS: --max_old_space_size=3584
artifacts:
expire_in: 7d
paths:
@@ -411,6 +415,7 @@ setup-test-env:
script:
- bundle exec ruby -Ispec -e 'require "spec_helper" ; TestEnv.init'
- scripts/gitaly-test-build # Do not use 'bundle exec' here
+ - BUNDLE_GEMFILE=Gemfile.rails5 bundle install $BUNDLE_INSTALL_FLAGS
artifacts:
expire_in: 7d
paths:
@@ -586,6 +591,12 @@ downtime_check:
- /(^docs[\/-].*|.*-docs$)/
- /(^qa[\/-].*|.*-qa$)/
+rails5_gemfile_lock_check:
+ <<: *dedicated-no-docs-no-db-pull-cache-job
+ <<: *except-docs-and-qa
+ script:
+ - scripts/rails5-gemfile-lock-check
+
ee_compat_check:
<<: *rake-exec
except:
@@ -658,10 +669,13 @@ gitlab:assets:compile:
SKIP_STORAGE_VALIDATION: "true"
WEBPACK_REPORT: "true"
NO_COMPRESSION: "true"
+ # we override the max_old_space_size to prevent OOM errors
+ NODE_OPTIONS: --max_old_space_size=3584
script:
- date
- yarn install --frozen-lockfile --production --cache-folder .yarn-cache
- date
+ - free -m
- bundle exec rake gitlab:assets:compile
artifacts:
name: webpack-report
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 82d1abff4a4..fb78973a727 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -301,7 +301,7 @@ For guidance on UX implementation at GitLab, please refer to our [Design System]
The UX team uses labels to manage their workflow.
The ~"UX" label on an issue is a signal to the UX team that it will need UX attention.
-To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/ux/) of the handbook.
+To better understand the priority by which UX tackles issues, see the [UX section](https://about.gitlab.com/handbook/engineering/ux) of the handbook.
Once an issue has been worked on and is ready for development, a UXer applies the ~"UX ready" label to that issue.
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index f1b9cc4cd95..c64d9d48a48 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.105.0
+0.105.1
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 80895903a15..f77856a6f1a 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-4.3.0
+4.3.1
diff --git a/Gemfile b/Gemfile
index 4c63f4c10b8..98622cdde84 100644
--- a/Gemfile
+++ b/Gemfile
@@ -108,6 +108,7 @@ gem 'hamlit', '~> 2.6.1'
# Files attachments
gem 'carrierwave', '~> 1.2'
+gem 'mini_magick'
# Drag and Drop UI
gem 'dropzonejs-rails', '~> 0.7.1'
@@ -133,7 +134,7 @@ gem 'seed-fu', '~> 2.3.7'
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.7.1'
gem 'deckar01-task_list', '2.0.0'
-gem 'gitlab-markup', '~> 1.6.2'
+gem 'gitlab-markup', '~> 1.6.4'
gem 'redcarpet', '~> 3.4'
gem 'commonmarker', '~> 0.17'
gem 'RedCloth', '~> 4.3.2'
@@ -332,7 +333,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.5.0'
gem 'factory_bot_rails', '~> 4.8.2'
- gem 'rspec-rails', '~> 3.6.0'
+ gem 'rspec-rails', '~> 3.7.0'
gem 'rspec-retry', '~> 0.4.5'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
@@ -408,18 +409,17 @@ gem 'vmstat', '~> 2.3.0'
gem 'sys-filesystem', '~> 1.1.6'
# SSH host key support
-gem 'net-ssh', '~> 4.2.0'
+gem 'net-ssh', '~> 5.0'
gem 'sshkey', '~> 1.9.0'
# Required for ED25519 SSH host key support
group :ed25519 do
- gem 'rbnacl-libsodium'
- gem 'rbnacl', '~> 4.0'
+ gem 'ed25519', '~> 1.2'
gem 'bcrypt_pbkdf', '~> 1.0'
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.100.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.101.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
diff --git a/Gemfile.lock b/Gemfile.lock
index 334895351ac..883e580b86b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -177,6 +177,7 @@ GEM
json-jwt (~> 1.6)
dropzonejs-rails (0.7.2)
rails (> 3.1)
+ ed25519 (1.2.4)
email_reply_trimmer (0.1.6)
email_spec (2.2.0)
htmlentities (~> 4.3.3)
@@ -282,7 +283,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.100.0)
+ gitaly-proto (0.101.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -295,7 +296,7 @@ GEM
flowdock (~> 0.7)
gitlab-grit (>= 2.4.1)
multi_json
- gitlab-gollum-lib (4.2.7.2)
+ gitlab-gollum-lib (4.2.7.4)
gemojione (~> 3.2)
github-markup (~> 1.6)
gollum-grit_adapter (~> 1.0)
@@ -303,7 +304,7 @@ GEM
rouge (~> 3.1)
sanitize (~> 2.1)
stringex (~> 2.6)
- gitlab-gollum-rugged_adapter (0.4.4)
+ gitlab-gollum-rugged_adapter (0.4.4.1)
mime-types (>= 1.15)
rugged (~> 0.25)
gitlab-grit (2.8.2)
@@ -311,7 +312,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16)
posix-spawn (~> 0.3)
- gitlab-markup (1.6.3)
+ gitlab-markup (1.6.4)
gitlab-styles (2.3.2)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
@@ -359,8 +360,8 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- grape-path-helpers (1.0.2)
- activesupport (~> 4)
+ grape-path-helpers (1.0.5)
+ activesupport (>= 4, < 5.1)
grape (~> 1.0)
rake (~> 12)
grape_logging (1.7.0)
@@ -451,7 +452,6 @@ GEM
kgio (2.10.0)
knapsack (1.16.0)
rake
- timecop (>= 0.1.0)
kubeclient (3.1.0)
http (~> 2.2.2)
recursive-open-struct (~> 1.0, >= 1.0.4)
@@ -498,6 +498,7 @@ GEM
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
mimemagic (0.3.0)
+ mini_magick (4.8.0)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.7.0)
@@ -510,7 +511,7 @@ GEM
mustermann (~> 1.0.0)
mysql2 (0.4.10)
net-ldap (0.16.0)
- net-ssh (4.2.0)
+ net-ssh (5.0.1)
netrc (0.11.0)
nokogiri (1.8.2)
mini_portile2 (~> 2.3.0)
@@ -696,10 +697,6 @@ GEM
ffi (>= 0.5.0, < 2)
rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
- rbnacl (4.0.2)
- ffi
- rbnacl-libsodium (1.0.11)
- rbnacl (>= 3.0.1)
rdoc (6.0.4)
re2 (1.1.1)
recaptcha (3.0.0)
@@ -745,36 +742,36 @@ GEM
chunky_png
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
- rspec (3.6.0)
- rspec-core (~> 3.6.0)
- rspec-expectations (~> 3.6.0)
- rspec-mocks (~> 3.6.0)
- rspec-core (3.6.0)
- rspec-support (~> 3.6.0)
- rspec-expectations (3.6.0)
+ rspec (3.7.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-core (3.7.1)
+ rspec-support (~> 3.7.0)
+ rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.6.0)
- rspec-mocks (3.6.0)
+ rspec-support (~> 3.7.0)
+ rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.6.0)
+ rspec-support (~> 3.7.0)
rspec-parameterized (0.4.0)
binding_of_caller
parser
proc_to_ast
rspec (>= 2.13, < 4)
unparser
- rspec-rails (3.6.0)
+ rspec-rails (3.7.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
- rspec-core (~> 3.6.0)
- rspec-expectations (~> 3.6.0)
- rspec-mocks (~> 3.6.0)
- rspec-support (~> 3.6.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-support (~> 3.7.0)
rspec-retry (0.4.5)
rspec-core
rspec-set (0.1.3)
- rspec-support (3.6.0)
+ rspec-support (3.7.1)
rspec_profiling (0.0.5)
activerecord
pg
@@ -1016,6 +1013,7 @@ DEPENDENCIES
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.3)
dropzonejs-rails (~> 0.7.1)
+ ed25519 (~> 1.2)
email_reply_trimmer (~> 0.1)
email_spec (~> 2.2.0)
factory_bot_rails (~> 4.8.2)
@@ -1041,12 +1039,12 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.100.0)
+ gitaly-proto (~> 0.101.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
- gitlab-markup (~> 1.6.2)
+ gitlab-markup (~> 1.6.4)
gitlab-styles (~> 2.3)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
@@ -1084,11 +1082,12 @@ DEPENDENCIES
loofah (~> 2.2)
mail_room (~> 0.9.1)
method_source (~> 0.8)
+ mini_magick
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.4.10)
net-ldap
- net-ssh (~> 4.2.0)
+ net-ssh (~> 5.0)
nokogiri (~> 1.8.2)
oauth2 (~> 1.4)
octokit (~> 4.9)
@@ -1130,8 +1129,6 @@ DEPENDENCIES
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
- rbnacl (~> 4.0)
- rbnacl-libsodium
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
@@ -1144,7 +1141,7 @@ DEPENDENCIES
rouge (~> 3.1)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
- rspec-rails (~> 3.6.0)
+ rspec-rails (~> 3.7.0)
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock
index 14ea3e4519c..952e27df29d 100644
--- a/Gemfile.rails5.lock
+++ b/Gemfile.rails5.lock
@@ -3,7 +3,7 @@ GEM
specs:
RedCloth (4.3.2)
abstract_type (0.0.7)
- ace-rails-ap (4.1.4)
+ ace-rails-ap (4.1.2)
actioncable (5.0.7)
actionpack (= 5.0.7)
nio4r (>= 1.2, < 3.0)
@@ -54,12 +54,12 @@ GEM
akismet (2.0.0)
allocations (1.0.5)
arel (7.1.4)
- asana (0.6.3)
+ asana (0.6.0)
faraday (~> 0.9)
faraday_middleware (~> 0.9)
faraday_middleware-multi_json (~> 0.0)
oauth2 (~> 1.0)
- asciidoctor (1.5.6.1)
+ asciidoctor (1.5.6.2)
asciidoctor-plantuml (0.0.8)
asciidoctor (~> 1.5)
asset_sync (2.4.0)
@@ -71,8 +71,8 @@ GEM
atomic (1.1.99)
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
- attr_required (1.0.1)
- awesome_print (1.2.0)
+ attr_required (1.0.0)
+ awesome_print (1.8.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
ice_nine (~> 0.11.0)
@@ -88,12 +88,12 @@ GEM
erubis (>= 2.6.6)
rack (>= 0.9.0)
bindata (2.4.3)
- binding_of_caller (0.7.3)
+ binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
blankslate (2.1.2.4)
bootstrap_form (2.7.0)
brakeman (4.2.1)
- browser (2.5.3)
+ browser (2.2.0)
builder (3.2.3)
bullet (5.5.1)
activesupport (>= 3.0.0)
@@ -102,32 +102,33 @@ GEM
bundler (~> 1.2)
thor (~> 0.18)
byebug (9.0.6)
- capybara (2.18.0)
+ capybara (2.15.1)
addressable
mini_mime (>= 0.1.3)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
rack-test (>= 0.5.4)
- xpath (>= 2.0, < 4.0)
- capybara-screenshot (1.0.18)
+ xpath (~> 2.0)
+ capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
- carrierwave (1.2.2)
+ carrierwave (1.2.1)
activemodel (>= 4.0.0)
activesupport (>= 4.0.0)
mime-types (>= 1.16)
- charlock_holmes (0.7.5)
+ cause (0.1)
+ charlock_holmes (0.7.6)
childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2)
chronic_duration (0.10.6)
numerizer (~> 0.1.1)
- chunky_png (1.3.10)
+ chunky_png (1.3.5)
citrus (3.0.2)
- coderay (1.1.2)
+ coderay (1.1.1)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
- commonmarker (0.17.9)
+ commonmarker (0.17.8)
ruby-enum (~> 0.5)
concord (0.1.5)
adamantium (~> 0.2.0)
@@ -140,11 +141,11 @@ GEM
safe_yaml (~> 1.0.0)
crass (1.0.4)
creole (0.5.0)
- css_parser (1.6.0)
+ css_parser (1.5.0)
addressable
- daemons (1.2.6)
+ daemons (1.2.3)
database_cleaner (1.5.3)
- debug_inspector (0.0.3)
+ debug_inspector (0.0.2)
debugger-ruby_core_source (1.3.8)
deckar01-task_list (2.0.0)
html-pipeline
@@ -154,32 +155,33 @@ GEM
activerecord (>= 3.2.0, < 5.2)
descendants_tracker (0.0.4)
thread_safe (~> 0.3, >= 0.3.1)
- device_detector (1.0.1)
- devise (4.4.1)
+ device_detector (1.0.0)
+ devise (4.4.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
- railties (>= 4.1.0, < 5.2)
+ railties (>= 4.1.0, < 6.0)
responders
warden (~> 1.2.3)
- devise-two-factor (3.0.2)
- activesupport (< 5.2)
+ devise-two-factor (3.0.0)
+ activesupport
attr_encrypted (>= 1.3, < 4, != 2)
devise (~> 4.0)
- railties (< 5.2)
+ railties
rotp (~> 2.0)
diff-lcs (1.3)
diffy (3.1.0)
docile (1.1.5)
domain_name (0.5.20180417)
unf (>= 0.0.5, < 1.0.0)
- doorkeeper (4.3.1)
+ doorkeeper (4.3.2)
railties (>= 4.2)
- doorkeeper-openid_connect (1.3.0)
+ doorkeeper-openid_connect (1.4.0)
doorkeeper (~> 4.3)
json-jwt (~> 1.6)
- dropzonejs-rails (0.7.4)
+ dropzonejs-rails (0.7.2)
rails (> 3.1)
- email_reply_trimmer (0.1.10)
+ ed25519 (1.2.4)
+ email_reply_trimmer (0.1.6)
email_spec (2.2.0)
htmlentities (~> 4.3.3)
launchy (~> 2.1)
@@ -188,11 +190,11 @@ GEM
equalizer (0.0.11)
erubis (2.7.0)
escape_utils (1.1.1)
- et-orbi (1.0.9)
+ et-orbi (1.0.3)
tzinfo
- eventmachine (1.2.5)
- excon (0.60.0)
- execjs (2.7.0)
+ eventmachine (1.0.8)
+ excon (0.62.0)
+ execjs (2.6.0)
expression_parser (0.9.0)
factory_bot (4.8.2)
activesupport (>= 3.0.0)
@@ -208,8 +210,8 @@ GEM
multi_json
fast_blank (1.0.0)
fast_gettext (1.6.0)
- ffaker (2.8.1)
- ffi (1.9.23)
+ ffaker (2.4.0)
+ ffi (1.9.18)
flay (2.10.0)
erubis (~> 2.7.0)
path_expander (~> 1.0)
@@ -247,13 +249,13 @@ GEM
fog-json (1.0.2)
fog-core (~> 1.0)
multi_json (~> 1.10)
- fog-local (0.5.0)
- fog-core (>= 1.27, < 3.0)
- fog-openstack (0.1.24)
- fog-core (~> 1.40)
+ fog-local (0.3.1)
+ fog-core (~> 1.27)
+ fog-openstack (0.1.21)
+ fog-core (>= 1.40)
fog-json (>= 1.0)
ipaddress (>= 0.8)
- fog-rackspace (0.1.5)
+ fog-rackspace (0.1.1)
fog-core (>= 1.35)
fog-json (>= 1.0)
fog-xml (>= 0.1)
@@ -261,8 +263,8 @@ GEM
fog-xml (0.1.3)
fog-core
nokogiri (>= 1.5.11, < 2.0.0)
- font-awesome-rails (4.7.0.3)
- railties (>= 3.2, < 5.2)
+ font-awesome-rails (4.7.0.1)
+ railties (>= 3.2, < 5.1)
foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
@@ -273,7 +275,7 @@ GEM
rugged (~> 0.21)
gemojione (3.3.0)
json
- get_process_mem (0.2.1)
+ get_process_mem (0.2.0)
gettext (3.2.9)
locale (>= 2.0.5)
text (>= 1.3.0)
@@ -284,7 +286,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (0.100.0)
+ gitaly-proto (0.101.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
@@ -313,7 +315,7 @@ GEM
diff-lcs (~> 1.1)
mime-types (>= 1.16)
posix-spawn (~> 0.3)
- gitlab-markup (1.6.3)
+ gitlab-markup (1.6.4)
gitlab-styles (2.3.2)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
@@ -349,9 +351,9 @@ GEM
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
- gpgme (2.0.16)
- mini_portile2 (~> 2.3)
- grape (1.0.2)
+ gpgme (2.0.13)
+ mini_portile2 (~> 2.1)
+ grape (1.0.3)
activesupport
builder
mustermann-grape (~> 1.0.0)
@@ -361,12 +363,16 @@ GEM
grape-entity (0.7.1)
activesupport (>= 4.0)
multi_json (>= 1.3.2)
- grape-path-helpers (1.0.0)
- activesupport
+ grape-path-helpers (1.0.5)
+ activesupport (>= 4, < 5.1)
grape (~> 1.0)
- rake
+ rake (~> 12)
grape_logging (1.7.0)
grape
+ graphiql-rails (1.4.10)
+ railties
+ sprockets-rails
+ graphql (1.8.1)
grpc (1.11.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
@@ -379,23 +385,23 @@ GEM
rake (>= 10, < 13)
rubocop (>= 0.49.0)
sysexits (~> 1.1)
- hamlit (2.6.2)
+ hamlit (2.6.1)
temple (~> 0.7.6)
thor
tilt
- hashdiff (0.3.7)
+ hashdiff (0.3.4)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0)
health_check (2.6.0)
rails (>= 4.0)
- hipchat (1.5.4)
+ hipchat (1.5.2)
httparty
mimemagic
html-pipeline (2.7.1)
activesupport (>= 2)
nokogiri (>= 1.4)
- html2text (0.2.1)
+ html2text (0.2.0)
nokogiri (~> 1.6)
htmlentities (4.3.4)
http (2.2.2)
@@ -415,9 +421,11 @@ GEM
concurrent-ruby (~> 1.0)
icalendar (2.4.1)
ice_nine (0.11.2)
- influxdb (0.5.3)
+ influxdb (0.2.3)
+ cause
+ json
ipaddress (0.8.3)
- jira-ruby (1.5.0)
+ jira-ruby (1.4.1)
activesupport
multipart-post
oauth (~> 0.5, >= 0.5.0)
@@ -432,30 +440,30 @@ GEM
json-schema (2.8.0)
addressable (>= 2.4)
jwt (1.5.6)
- kaminari (1.1.1)
+ kaminari (1.0.1)
activesupport (>= 4.1.0)
- kaminari-actionview (= 1.1.1)
- kaminari-activerecord (= 1.1.1)
- kaminari-core (= 1.1.1)
- kaminari-actionview (1.1.1)
+ kaminari-actionview (= 1.0.1)
+ kaminari-activerecord (= 1.0.1)
+ kaminari-core (= 1.0.1)
+ kaminari-actionview (1.0.1)
actionview
- kaminari-core (= 1.1.1)
- kaminari-activerecord (1.1.1)
+ kaminari-core (= 1.0.1)
+ kaminari-activerecord (1.0.1)
activerecord
- kaminari-core (= 1.1.1)
- kaminari-core (1.1.1)
- kgio (2.11.2)
+ kaminari-core (= 1.0.1)
+ kaminari-core (1.0.1)
+ kgio (2.10.0)
knapsack (1.16.0)
rake
- kubeclient (3.1.1)
+ kubeclient (3.1.0)
http (~> 2.2.2)
recursive-open-struct (~> 1.0, >= 1.0.4)
rest-client (~> 2.0)
launchy (2.4.3)
addressable (~> 2.3)
- letter_opener (1.6.0)
+ letter_opener (1.4.1)
launchy (~> 2.2)
- letter_opener_web (1.3.3)
+ letter_opener_web (1.3.0)
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
@@ -474,7 +482,7 @@ GEM
logging (2.2.2)
little-plugger (~> 1.1)
multi_json (~> 1.10)
- lograge (0.9.0)
+ lograge (0.10.0)
actionpack (>= 4)
activesupport (>= 4)
railties (>= 4)
@@ -488,11 +496,12 @@ GEM
memoist (0.16.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
- method_source (0.9.0)
+ method_source (0.8.2)
mime-types (3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2016.0521)
- mimemagic (0.3.2)
+ mimemagic (0.3.0)
+ mini_magick (4.8.0)
mini_mime (1.0.0)
mini_portile2 (2.3.0)
minitest (5.7.0)
@@ -504,8 +513,8 @@ GEM
mustermann-grape (1.0.0)
mustermann (~> 1.0.0)
mysql2 (0.4.10)
- net-ldap (0.16.1)
- net-ssh (4.2.0)
+ net-ldap (0.16.0)
+ net-ssh (5.0.1)
netrc (0.11.0)
nio4r (2.3.1)
nokogiri (1.8.2)
@@ -559,7 +568,7 @@ GEM
omniauth-oauth2 (1.5.0)
oauth2 (~> 1.1)
omniauth (~> 1.2)
- omniauth-oauth2-generic (0.2.4)
+ omniauth-oauth2-generic (0.2.2)
omniauth-oauth2 (~> 1.0)
omniauth-saml (1.10.0)
omniauth (~> 1.3, >= 1.3.2)
@@ -578,7 +587,7 @@ GEM
orm_adapter (0.5.0)
os (0.9.6)
parallel (1.12.1)
- parser (2.5.0.5)
+ parser (2.5.1.0)
ast (~> 2.4.0)
parslet (1.5.0)
blankslate (~> 2.0)
@@ -614,9 +623,9 @@ GEM
json (>= 1.6.0)
posix-spawn (0.3.13)
powerpack (0.1.1)
- premailer (1.11.1)
+ premailer (1.10.4)
addressable
- css_parser (>= 1.6.0)
+ css_parser (>= 1.4.10)
htmlentities (>= 4.0.0)
premailer-rails (1.9.7)
actionmailer (>= 3, < 6)
@@ -627,14 +636,15 @@ GEM
unparser
procto (0.0.3)
prometheus-client-mmap (0.9.3)
- pry (0.11.3)
+ pry (0.10.4)
coderay (~> 1.1.0)
- method_source (~> 0.9.0)
- pry-byebug (3.4.3)
- byebug (>= 9.0, < 9.1)
+ method_source (~> 0.8.1)
+ slop (~> 3.4)
+ pry-byebug (3.4.2)
+ byebug (~> 9.0)
pry (~> 0.10)
- pry-rails (0.3.6)
- pry (>= 0.10.4)
+ pry-rails (0.3.5)
+ pry (>= 0.9.10)
public_suffix (3.0.2)
pyu-ruby-sasl (0.0.3.3)
rack (2.0.5)
@@ -651,7 +661,7 @@ GEM
rack (>= 1.1)
rack-protection (2.0.1)
rack
- rack-proxy (0.6.4)
+ rack-proxy (0.6.0)
rack
rack-test (0.6.3)
rack (>= 1.0)
@@ -689,20 +699,16 @@ GEM
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
- raindrops (0.19.0)
+ raindrops (0.18.0)
rake (12.3.1)
- rb-fsevent (0.10.3)
+ rb-fsevent (0.10.2)
rb-inotify (0.9.10)
ffi (>= 0.5.0, < 2)
- rblineprof (0.3.7)
+ rblineprof (0.3.6)
debugger-ruby_core_source (~> 1.3)
- rbnacl (4.0.2)
- ffi
- rbnacl-libsodium (1.0.16)
- rbnacl (>= 3.0.1)
rdoc (6.0.4)
re2 (1.1.1)
- recaptcha (3.4.0)
+ recaptcha (3.0.0)
json
recursive-open-struct (1.1.0)
redcarpet (3.4.0)
@@ -729,8 +735,7 @@ GEM
declarative (< 0.1.0)
declarative-option (< 0.2.0)
uber (< 0.2.0)
- request_store (1.4.0)
- rack (>= 1.4)
+ request_store (1.3.1)
responders (2.4.0)
actionpack (>= 4.2.0, < 5.3)
railties (>= 4.2.0, < 5.3)
@@ -739,43 +744,43 @@ GEM
mime-types (>= 1.16, < 4.0)
netrc (~> 0.8)
retriable (3.1.1)
- rinku (2.0.4)
+ rinku (2.0.0)
rotp (2.1.2)
rouge (3.1.1)
- rqrcode (0.10.1)
- chunky_png (~> 1.0)
+ rqrcode (0.7.0)
+ chunky_png
rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2)
- rspec (3.6.0)
- rspec-core (~> 3.6.0)
- rspec-expectations (~> 3.6.0)
- rspec-mocks (~> 3.6.0)
- rspec-core (3.6.0)
- rspec-support (~> 3.6.0)
- rspec-expectations (3.6.0)
+ rspec (3.7.0)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-core (3.7.1)
+ rspec-support (~> 3.7.0)
+ rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.6.0)
- rspec-mocks (3.6.0)
+ rspec-support (~> 3.7.0)
+ rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.6.0)
+ rspec-support (~> 3.7.0)
rspec-parameterized (0.4.0)
binding_of_caller
parser
proc_to_ast
rspec (>= 2.13, < 4)
unparser
- rspec-rails (3.6.1)
+ rspec-rails (3.7.2)
actionpack (>= 3.0)
activesupport (>= 3.0)
railties (>= 3.0)
- rspec-core (~> 3.6.0)
- rspec-expectations (~> 3.6.0)
- rspec-mocks (~> 3.6.0)
- rspec-support (~> 3.6.0)
- rspec-retry (0.4.6)
+ rspec-core (~> 3.7.0)
+ rspec-expectations (~> 3.7.0)
+ rspec-mocks (~> 3.7.0)
+ rspec-support (~> 3.7.0)
+ rspec-retry (0.4.5)
rspec-core
rspec-set (0.1.3)
- rspec-support (3.6.0)
+ rspec-support (3.7.1)
rspec_profiling (0.0.5)
activerecord
pg
@@ -790,7 +795,7 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-rspec (1.22.2)
+ rubocop-rspec (1.22.1)
rubocop (>= 0.52.1)
ruby-enum (0.7.2)
i18n
@@ -800,14 +805,14 @@ GEM
ruby-progressbar (1.9.0)
ruby-saml (1.7.2)
nokogiri (>= 1.5.10)
- ruby_parser (3.11.0)
- sexp_processor (~> 4.9)
+ ruby_parser (3.9.0)
+ sexp_processor (~> 4.1)
rubyntlm (0.6.2)
- rubypants (0.7.0)
+ rubypants (0.2.0)
rubyzip (1.2.1)
- rufus-scheduler (3.4.2)
+ rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
- rugged (0.27.0)
+ rugged (0.27.1)
safe_yaml (1.0.4)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
@@ -816,7 +821,7 @@ GEM
sass-listen (4.0.0)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
- sass-rails (5.0.7)
+ sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
@@ -832,7 +837,7 @@ GEM
seed-fu (2.3.7)
activerecord (>= 3.1)
activesupport (>= 3.1)
- select2-rails (3.5.10)
+ select2-rails (3.5.9.3)
thor (~> 0.14)
selenium-webdriver (3.12.0)
childprocess (~> 0.5)
@@ -840,17 +845,17 @@ GEM
sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
- sexp_processor (4.10.1)
+ sexp_processor (4.9.0)
sham_rack (1.3.6)
rack
shoulda-matchers (3.1.2)
activesupport (>= 4.0.0)
- sidekiq (5.1.1)
+ sidekiq (5.1.3)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
- sidekiq-cron (0.6.3)
+ sidekiq-cron (0.6.0)
rufus-scheduler (>= 3.3.0)
sidekiq (>= 4.2.1)
sidekiq-limit_fetch (3.4.0)
@@ -860,14 +865,15 @@ GEM
faraday (~> 0.9)
jwt (>= 1.5, < 3.0)
multi_json (~> 1.10)
- simple_po_parser (1.1.3)
+ simple_po_parser (1.1.2)
simplecov (0.14.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
- simplecov-html (0.10.2)
+ simplecov-html (0.10.0)
slack-notifier (1.5.1)
- spring (2.0.2)
+ slop (3.6.0)
+ spring (2.0.1)
activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
@@ -880,7 +886,7 @@ GEM
sprockets (>= 3.0.0)
sqlite3 (1.3.13)
sshkey (1.9.0)
- stackprof (0.2.11)
+ stackprof (0.2.10)
state_machines (0.5.0)
state_machines-activemodel (0.5.1)
activemodel (>= 4.1, < 6.0)
@@ -889,19 +895,19 @@ GEM
activerecord (>= 4.1, < 6.0)
state_machines-activemodel (>= 0.5.0)
stringex (2.8.4)
- sys-filesystem (1.1.9)
+ sys-filesystem (1.1.6)
ffi
sysexits (1.2.0)
temple (0.7.7)
test-prof (0.2.5)
text (1.3.1)
- thin (1.7.2)
+ thin (1.7.0)
daemons (~> 1.0, >= 1.0.9)
eventmachine (~> 1.0, >= 1.0.4)
rack (>= 1, < 3)
thor (0.19.4)
thread_safe (0.3.6)
- tilt (2.0.8)
+ tilt (2.0.6)
timecop (0.8.1)
timfel-krb5-auth (0.8.3)
toml (0.1.2)
@@ -921,7 +927,7 @@ GEM
unf (0.1.4)
unf_ext
unf_ext (0.0.7.5)
- unicode-display_width (1.3.0)
+ unicode-display_width (1.3.2)
unicorn (5.1.0)
kgio (~> 2.6)
raindrops (~> 0.7)
@@ -938,7 +944,7 @@ GEM
parser (>= 2.3.1.2, < 2.6)
procto (~> 0.0.2)
url_safe_base64 (0.2.2)
- validates_hostname (1.0.8)
+ validates_hostname (1.0.6)
activerecord (>= 3.0)
activesupport (>= 3.0)
version_sorter (2.1.0)
@@ -954,7 +960,7 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
- webpack-rails (0.9.11)
+ webpack-rails (0.9.10)
railties (>= 3.2.0)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
@@ -965,8 +971,8 @@ GEM
rinku
with_env (1.1.0)
xml-simple (1.1.5)
- xpath (3.0.0)
- nokogiri (~> 1.8)
+ xpath (2.1.0)
+ nokogiri (~> 1.3)
PLATFORMS
ruby
@@ -1017,6 +1023,7 @@ DEPENDENCIES
doorkeeper (~> 4.3)
doorkeeper-openid_connect (~> 1.3)
dropzonejs-rails (~> 0.7.1)
+ ed25519 (~> 1.2)
email_reply_trimmer (~> 0.1)
email_spec (~> 2.2.0)
factory_bot_rails (~> 4.8.2)
@@ -1042,12 +1049,12 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 0.100.0)
+ gitaly-proto (~> 0.101.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
gitlab-gollum-rugged_adapter (~> 0.4.4)
- gitlab-markup (~> 1.6.2)
+ gitlab-markup (~> 1.6.4)
gitlab-styles (~> 2.3)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
@@ -1058,6 +1065,8 @@ DEPENDENCIES
grape-entity (~> 0.7.1)
grape-path-helpers (~> 1.0)
grape_logging (~> 1.7)
+ graphiql-rails (~> 1.4.10)
+ graphql (~> 1.8.0)
grpc (~> 1.11.0)
haml_lint (~> 0.26.0)
hamlit (~> 2.6.1)
@@ -1083,11 +1092,12 @@ DEPENDENCIES
loofah (~> 2.2)
mail_room (~> 0.9.1)
method_source (~> 0.8)
+ mini_magick
minitest (~> 5.7.0)
mousetrap-rails (~> 1.4.6)
mysql2 (~> 0.4.10)
net-ldap
- net-ssh (~> 4.2.0)
+ net-ssh (~> 5.0)
nokogiri (~> 1.8.2)
oauth2 (~> 1.4)
octokit (~> 4.9)
@@ -1130,8 +1140,6 @@ DEPENDENCIES
rainbow (~> 2.2)
raindrops (~> 0.18)
rblineprof (~> 0.3.6)
- rbnacl (~> 4.0)
- rbnacl-libsodium
rdoc (~> 6.0)
re2 (~> 1.1.1)
recaptcha (~> 3.0)
@@ -1144,7 +1152,7 @@ DEPENDENCIES
rouge (~> 3.1)
rqrcode-rails3 (~> 0.1.7)
rspec-parameterized
- rspec-rails (~> 3.6.0)
+ rspec-rails (~> 3.7.0)
rspec-retry (~> 0.4.5)
rspec-set (~> 0.1.3)
rspec_profiling (~> 0.0.5)
diff --git a/INSTALLATION_TYPE b/INSTALLATION_TYPE
new file mode 100644
index 00000000000..5a18cd2fbf6
--- /dev/null
+++ b/INSTALLATION_TYPE
@@ -0,0 +1 @@
+source
diff --git a/PROCESS.md b/PROCESS.md
index 7438df8014b..a46fd8c25b4 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -169,6 +169,7 @@ the stable branch are:
* Fixes for [regressions](#regressions)
* Fixes for security issues
* Fixes or improvements to automated QA scenarios
+* Documentation updates for changes in the same release
* New or updated translations (as long as they do not touch application code)
During the feature freeze all merge requests that are meant to go into the
@@ -185,11 +186,7 @@ next patch release.
If a merge request is to be picked into more than one release it will need one
`Pick into X.Y` label per release where the merge request should be back-ported
-to.
-
-For example, if the current patch release is `10.1.1` and a regression fix needs
-to be backported down to the `9.5` release, you will need to assign it the
-`10.1` milestone and the following labels:
+to. For example:
- `Pick into 10.1`
- `Pick into 10.0`
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico b/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico
deleted file mode 100644
index 4af3582b60d..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_canceled.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_created.ico b/app/assets/images/ci_favicons/dev/favicon_status_created.ico
deleted file mode 100644
index 13639da2e8a..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_created.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico b/app/assets/images/ci_favicons/dev/favicon_status_failed.ico
deleted file mode 100644
index 5f0e711b104..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_failed.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico b/app/assets/images/ci_favicons/dev/favicon_status_manual.ico
deleted file mode 100644
index 8b1168a1267..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_manual.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico b/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico
deleted file mode 100644
index ed19b69e1c5..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_not_found.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico b/app/assets/images/ci_favicons/dev/favicon_status_pending.ico
deleted file mode 100644
index 5dfefd4cc5a..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_pending.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_running.ico b/app/assets/images/ci_favicons/dev/favicon_status_running.ico
deleted file mode 100644
index a41539c0e3e..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_running.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico b/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico
deleted file mode 100644
index 2c1ae552b93..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_skipped.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_success.ico b/app/assets/images/ci_favicons/dev/favicon_status_success.ico
deleted file mode 100644
index 70f0ca61eca..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_success.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico b/app/assets/images/ci_favicons/dev/favicon_status_warning.ico
deleted file mode 100644
index db289e03eb1..00000000000
--- a/app/assets/images/ci_favicons/dev/favicon_status_warning.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.ico b/app/assets/images/ci_favicons/favicon_status_canceled.ico
deleted file mode 100644
index 23adcffff50..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_canceled.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_canceled.png b/app/assets/images/ci_favicons/favicon_status_canceled.png
new file mode 100644
index 00000000000..8adaa9c600b
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_canceled.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_created.ico b/app/assets/images/ci_favicons/favicon_status_created.ico
deleted file mode 100644
index f9d93b390d8..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_created.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_created.png b/app/assets/images/ci_favicons/favicon_status_created.png
new file mode 100644
index 00000000000..ca788dd0034
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_created.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_failed.ico b/app/assets/images/ci_favicons/favicon_status_failed.ico
deleted file mode 100644
index 28a22ebf724..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_failed.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_failed.png b/app/assets/images/ci_favicons/favicon_status_failed.png
new file mode 100644
index 00000000000..93f1e2772fd
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_failed.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_manual.ico b/app/assets/images/ci_favicons/favicon_status_manual.ico
deleted file mode 100644
index dbbf1abf30c..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_manual.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_manual.png b/app/assets/images/ci_favicons/favicon_status_manual.png
new file mode 100644
index 00000000000..c926062c806
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_manual.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.ico b/app/assets/images/ci_favicons/favicon_status_not_found.ico
deleted file mode 100644
index 49b9b232dd1..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_not_found.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_not_found.png b/app/assets/images/ci_favicons/favicon_status_not_found.png
new file mode 100644
index 00000000000..df3049315a9
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_not_found.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_pending.ico b/app/assets/images/ci_favicons/favicon_status_pending.ico
deleted file mode 100644
index 05962f3f148..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_pending.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_pending.png b/app/assets/images/ci_favicons/favicon_status_pending.png
new file mode 100644
index 00000000000..f7d67d4a230
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_pending.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_running.ico b/app/assets/images/ci_favicons/favicon_status_running.ico
deleted file mode 100644
index 7fa3d4d48d4..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_running.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_running.png b/app/assets/images/ci_favicons/favicon_status_running.png
new file mode 100644
index 00000000000..ff4167c4b20
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_running.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.ico b/app/assets/images/ci_favicons/favicon_status_skipped.ico
deleted file mode 100644
index b0c26b62068..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_skipped.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_skipped.png b/app/assets/images/ci_favicons/favicon_status_skipped.png
new file mode 100644
index 00000000000..a9c36464b69
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_skipped.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_success.ico b/app/assets/images/ci_favicons/favicon_status_success.ico
deleted file mode 100644
index b150960b5be..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_success.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_success.png b/app/assets/images/ci_favicons/favicon_status_success.png
new file mode 100644
index 00000000000..bcc30c73f5f
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_success.png
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_warning.ico b/app/assets/images/ci_favicons/favicon_status_warning.ico
deleted file mode 100644
index 7e71d71684d..00000000000
--- a/app/assets/images/ci_favicons/favicon_status_warning.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_warning.png b/app/assets/images/ci_favicons/favicon_status_warning.png
new file mode 100644
index 00000000000..6db3b0280f5
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_warning.png
Binary files differ
diff --git a/app/assets/images/favicon-blue.ico b/app/assets/images/favicon-blue.ico
deleted file mode 100644
index 156fcf07588..00000000000
--- a/app/assets/images/favicon-blue.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/favicon-blue.png b/app/assets/images/favicon-blue.png
new file mode 100644
index 00000000000..2229fe79462
--- /dev/null
+++ b/app/assets/images/favicon-blue.png
Binary files differ
diff --git a/app/assets/images/favicon-yellow.ico b/app/assets/images/favicon-yellow.ico
deleted file mode 100644
index b650f277fb6..00000000000
--- a/app/assets/images/favicon-yellow.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/favicon-yellow.png b/app/assets/images/favicon-yellow.png
new file mode 100644
index 00000000000..2d5289818b4
--- /dev/null
+++ b/app/assets/images/favicon-yellow.png
Binary files differ
diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico
deleted file mode 100644
index 3479cbbb46f..00000000000
--- a/app/assets/images/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/app/assets/images/favicon.png b/app/assets/images/favicon.png
new file mode 100644
index 00000000000..845e0ec34a5
--- /dev/null
+++ b/app/assets/images/favicon.png
Binary files differ
diff --git a/app/assets/javascripts/badges/components/badge.vue b/app/assets/javascripts/badges/components/badge.vue
index d0f60e1d4cb..b4bfaee1d85 100644
--- a/app/assets/javascripts/badges/components/badge.vue
+++ b/app/assets/javascripts/badges/components/badge.vue
@@ -72,11 +72,11 @@ export default {
rel="noopener noreferrer"
>
<img
- class="project-badge"
:src="imageUrlWithRetries"
+ class="project-badge"
+ aria-hidden="true"
@load="onLoad"
@error="onError"
- aria-hidden="true"
/>
</a>
@@ -91,9 +91,9 @@ export default {
>
<div class="btn btn-default btn-sm disabled">
<icon
+ :size="16"
class="prepend-left-8 append-right-8"
name="doc_image"
- :size="16"
aria-hidden="true"
/>
</div>
@@ -105,16 +105,16 @@ export default {
</div>
<button
+ v-tooltip
v-show="hasError"
+ :title="s__('Badges|Reload badge image')"
class="btn btn-transparent btn-sm text-primary"
type="button"
- v-tooltip
- :title="s__('Badges|Reload badge image')"
@click="reloadImage"
>
<icon
- name="retry"
:size="16"
+ name="retry"
/>
</button>
</div>
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index 5975cb9669e..7a13f74c570 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -153,10 +153,10 @@ export default {
<label for="badge-link-url">{{ s__('Badges|Link') }}</label>
<input
id="badge-link-url"
- type="text"
- class="form-control"
v-model="linkUrl"
:placeholder="$options.badgeLinkUrlPlaceholder"
+ type="text"
+ class="form-control"
@input="debouncedPreview"
/>
<span
@@ -169,10 +169,10 @@ export default {
<label for="badge-image-url">{{ s__('Badges|Badge image URL') }}</label>
<input
id="badge-image-url"
- type="text"
- class="form-control"
v-model="imageUrl"
:placeholder="$options.badgeImageUrlPlaceholder"
+ type="text"
+ class="form-control"
@input="debouncedPreview"
/>
<span
@@ -184,8 +184,8 @@ export default {
<div class="form-group">
<label for="badge-preview">{{ s__('Badges|Badge image preview') }}</label>
<badge
- id="badge-preview"
v-show="renderedBadge && !isRendering"
+ id="badge-preview"
:image-url="renderedImageUrl"
:link-url="renderedLinkUrl"
/>
@@ -202,16 +202,16 @@ export default {
<div class="row-content-block">
<loading-button
- type="submit"
- container-class="btn btn-success"
:disabled="!canSubmit"
:loading="isSaving"
:label="submitButtonLabel"
+ type="submit"
+ container-class="btn btn-success"
/>
<button
+ v-if="isEditing"
class="btn btn-cancel"
type="button"
- v-if="isEditing"
@click="onCancel"
>{{ __('Cancel') }}</button>
</div>
diff --git a/app/assets/javascripts/badges/components/badge_list_row.vue b/app/assets/javascripts/badges/components/badge_list_row.vue
index af062bdf8c6..98aa00af0d7 100644
--- a/app/assets/javascripts/badges/components/badge_list_row.vue
+++ b/app/assets/javascripts/badges/components/badge_list_row.vue
@@ -41,9 +41,9 @@ export default {
<template>
<div class="gl-responsive-table-row-layout gl-responsive-table-row">
<badge
- class="table-section section-30"
:image-url="badge.renderedImageUrl"
:link-url="badge.renderedLinkUrl"
+ class="table-section section-30"
/>
<span class="table-section section-50 str-truncated">{{ badge.linkUrl }}</span>
<div class="table-section section-10">
@@ -54,29 +54,29 @@ export default {
v-if="canEditBadge"
class="table-action-buttons">
<button
+ :disabled="badge.isDeleting"
class="btn btn-default append-right-8"
type="button"
- :disabled="badge.isDeleting"
@click="editBadge(badge)"
>
<icon
- name="pencil"
:size="16"
:aria-label="__('Edit')"
+ name="pencil"
/>
</button>
<button
+ :disabled="badge.isDeleting"
class="btn btn-danger"
type="button"
data-toggle="modal"
data-target="#delete-badge-modal"
- :disabled="badge.isDeleting"
@click="updateBadgeInModal(badge)"
>
<icon
- name="remove"
:size="16"
:aria-label="__('Delete')"
+ name="remove"
/>
</button>
<loading-icon
diff --git a/app/assets/javascripts/badges/components/badge_settings.vue b/app/assets/javascripts/badges/components/badge_settings.vue
index 83f78394238..cc47e56dd1e 100644
--- a/app/assets/javascripts/badges/components/badge_settings.vue
+++ b/app/assets/javascripts/badges/components/badge_settings.vue
@@ -44,8 +44,8 @@ export default {
<gl-modal
id="delete-badge-modal"
:header-title-text="s__('Badges|Delete badge?')"
- footer-primary-button-variant="danger"
:footer-primary-button-text="s__('Badges|Delete badge')"
+ footer-primary-button-variant="danger"
@submit="onSubmitModal">
<div class="well">
<badge
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 75cf90de0b5..1ea6dd909e9 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -119,7 +119,7 @@ const gfmRules = {
return el.outerHTML;
},
'dl'(el, text) {
- let lines = text.trim().split('\n');
+ let lines = text.replace(/\n\n/g, '\n').trim().split('\n');
// Add two spaces to the front of subsequent list items lines,
// or leave the line entirely blank.
lines = lines.map((l) => {
@@ -129,9 +129,13 @@ const gfmRules = {
return ` ${line}`;
});
- return `<dl>\n${lines.join('\n')}\n</dl>`;
+ return `<dl>\n${lines.join('\n')}\n</dl>\n`;
},
- 'sub, dt, dd, kbd, q, samp, var, ruby, rt, rp, abbr, summary, details'(el, text) {
+ 'dt, dd, summary, details'(el, text) {
+ const tag = el.nodeName.toLowerCase();
+ return `<${tag}>${text}</${tag}>\n`;
+ },
+ 'sup, sub, kbd, q, samp, var, ruby, rt, rp, abbr'(el, text) {
const tag = el.nodeName.toLowerCase();
return `<${tag}>${text}</${tag}>`;
},
@@ -215,22 +219,22 @@ const gfmRules = {
return text.replace(/^- /mg, '1. ');
},
'h1'(el, text) {
- return `# ${text.trim()}`;
+ return `# ${text.trim()}\n`;
},
'h2'(el, text) {
- return `## ${text.trim()}`;
+ return `## ${text.trim()}\n`;
},
'h3'(el, text) {
- return `### ${text.trim()}`;
+ return `### ${text.trim()}\n`;
},
'h4'(el, text) {
- return `#### ${text.trim()}`;
+ return `#### ${text.trim()}\n`;
},
'h5'(el, text) {
- return `##### ${text.trim()}`;
+ return `##### ${text.trim()}\n`;
},
'h6'(el, text) {
- return `###### ${text.trim()}`;
+ return `###### ${text.trim()}\n`;
},
'strong'(el, text) {
return `**${text}**`;
@@ -241,11 +245,13 @@ const gfmRules = {
'del'(el, text) {
return `~~${text}~~`;
},
- 'sup'(el, text) {
- return `^${text}`;
- },
'hr'(el) {
- return '-----';
+ // extra leading \n is to ensure that there is a blank line between
+ // a list followed by an hr, otherwise this breaks old redcarpet rendering
+ return '\n-----\n';
+ },
+ 'p'(el, text) {
+ return `${text.trim()}\n`;
},
'table'(el) {
const theadEl = el.querySelector('thead');
@@ -263,7 +269,9 @@ const gfmRules = {
let before = '';
let after = '';
- switch (cell.style.textAlign) {
+ const alignment = cell.align || cell.style.textAlign;
+
+ switch (alignment) {
case 'center':
before = ':';
after = ':';
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index ac06d79fb60..86b888c66c8 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -14,17 +14,28 @@ window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.Board = Vue.extend({
- template: '#js-board-template',
components: {
boardList,
'board-delete': gl.issueBoards.BoardDelete,
BoardBlankState,
},
props: {
- list: Object,
- disabled: Boolean,
- issueLinkBase: String,
- rootPath: String,
+ list: {
+ type: Object,
+ default: () => ({}),
+ },
+ disabled: {
+ type: Boolean,
+ required: true,
+ },
+ issueLinkBase: {
+ type: String,
+ required: true,
+ },
+ rootPath: {
+ type: String,
+ required: true,
+ },
boardId: {
type: String,
required: true,
@@ -82,20 +93,6 @@ gl.issueBoards.Board = Vue.extend({
deep: true
}
},
- methods: {
- showNewIssueForm() {
- this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
- },
- toggleExpanded(e) {
- if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
- this.list.isExpanded = !this.list.isExpanded;
-
- if (AccessorUtilities.isLocalStorageAccessSafe()) {
- localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
- }
- }
- },
- },
mounted () {
this.sortableOptions = gl.issueBoards.getBoardSortableDefaultOptions({
disabled: this.disabled,
@@ -125,4 +122,19 @@ gl.issueBoards.Board = Vue.extend({
this.list.isExpanded = !isCollapsed;
}
},
+ methods: {
+ showNewIssueForm() {
+ this.$refs['board-list'].showIssueForm = !this.$refs['board-list'].showIssueForm;
+ },
+ toggleExpanded(e) {
+ if (this.list.isExpandable && !e.target.classList.contains('js-no-trigger-collapse')) {
+ this.list.isExpanded = !this.list.isExpanded;
+
+ if (AccessorUtilities.isLocalStorageAccessSafe()) {
+ localStorage.setItem(`boards.${this.boardId}.${this.list.type}.expanded`, this.list.isExpanded);
+ }
+ }
+ },
+ },
+ template: '#js-board-template',
});
diff --git a/app/assets/javascripts/boards/components/board_blank_state.vue b/app/assets/javascripts/boards/components/board_blank_state.vue
index 2049eeb9c30..286529b4d13 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.vue
+++ b/app/assets/javascripts/boards/components/board_blank_state.vue
@@ -72,8 +72,8 @@ export default {
:key="index"
>
<span
- class="label-color"
- :style="{ backgroundColor: label.color }">
+ :style="{ backgroundColor: label.color }"
+ class="label-color">
</span>
{{ label.title }}
</li>
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 33e3369b971..b7d3574bc80 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -77,7 +77,6 @@ export default {
<template>
<li
- class="board-card"
:class="{
'user-can-drag': !disabled && issue.id,
'is-disabled': disabled || !issue.id,
@@ -85,6 +84,7 @@ export default {
}"
:index="index"
:data-issue-id="issue.id"
+ class="board-card"
@mousedown="mouseDown"
@mousemove="mouseMove"
@mouseup="showIssue($event)">
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index 7be98825fda..4482b3b3e70 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -8,7 +8,10 @@ window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardDelete = Vue.extend({
props: {
- list: Object
+ list: {
+ type: Object,
+ default: () => ({}),
+ },
},
methods: {
deleteBoard () {
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 84a7f277227..5c7565234d8 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -87,10 +87,46 @@ export default {
mounted() {
const options = gl.issueBoards.getBoardSortableDefaultOptions({
scroll: true,
- group: 'issues',
disabled: this.disabled,
filter: '.board-list-count, .is-disabled',
dataIdAttr: 'data-issue-id',
+ group: {
+ name: 'issues',
+ /**
+ * Dynamically determine between which containers
+ * items can be moved or copied as
+ * Assignee lists (EE feature) require this behavior
+ */
+ pull: (to, from, dragEl, e) => {
+ // As per Sortable's docs, `to` should provide
+ // reference to exact sortable container on which
+ // we're trying to drag element, but either it is
+ // a library's bug or our markup structure is too complex
+ // that `to` never points to correct container
+ // See https://github.com/RubaXa/Sortable/issues/1037
+ //
+ // So we use `e.target` which is always accurate about
+ // which element we're currently dragging our card upon
+ // So from there, we can get reference to actual container
+ // and thus the container type to enable Copy or Move
+ if (e.target) {
+ const containerEl = e.target.closest('.js-board-list') || e.target.querySelector('.js-board-list');
+ const toBoardType = containerEl.dataset.boardType;
+
+ if (toBoardType) {
+ const fromBoardType = this.list.type;
+
+ if ((fromBoardType === 'assignee' && toBoardType === 'label') ||
+ (fromBoardType === 'label' && toBoardType === 'assignee')) {
+ return 'clone';
+ }
+ }
+ }
+
+ return true;
+ },
+ revertClone: true,
+ },
onStart: (e) => {
const card = this.$refs.issue[e.oldIndex];
@@ -169,21 +205,22 @@ export default {
<template>
<div class="board-list-component">
<div
+ v-if="loading"
class="board-list-loading text-center"
- aria-label="Loading issues"
- v-if="loading">
+ aria-label="Loading issues">
<loading-icon />
</div>
<board-new-issue
+ v-if="list.type !== 'closed' && showIssueForm"
:group-id="groupId"
- :list="list"
- v-if="list.type !== 'closed' && showIssueForm"/>
+ :list="list"/>
<ul
- class="board-list"
v-show="!loading"
ref="list"
:data-board="list.id"
- :class="{ 'is-smaller': showIssueForm }">
+ :data-board-type="list.type"
+ :class="{ 'is-smaller': showIssueForm }"
+ class="board-list js-board-list">
<board-card
v-for="(issue, index) in issues"
ref="issue"
@@ -196,8 +233,8 @@ export default {
:disabled="disabled"
:key="issue.id" />
<li
- class="board-list-count text-center"
v-if="showCount"
+ class="board-list-count text-center"
data-issue-id="-1">
<loading-icon
v-show="list.loadingMore"
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index e8dfd95f7ae..ec23b1e7c11 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -49,11 +49,12 @@ export default {
this.error = false;
const labels = this.list.label ? [this.list.label] : [];
+ const assignees = this.list.assignee ? [this.list.assignee] : [];
const issue = new ListIssue({
title: this.title,
labels,
subscribed: true,
- assignees: [],
+ assignees,
project_id: this.selectedProject.id,
});
@@ -95,26 +96,26 @@ export default {
<div class="board-card">
<form @submit="submit($event)">
<div
- class="flash-container"
v-if="error"
+ class="flash-container"
>
<div class="flash-alert">
An error occurred. Please try again.
</div>
</div>
<label
- class="label-light"
:for="list.id + '-title'"
+ class="label-light"
>
Title
</label>
<input
+ ref="input"
+ v-model="title"
+ :id="list.id + '-title'"
class="form-control"
type="text"
- v-model="title"
- ref="input"
autocomplete="off"
- :id="list.id + '-title'"
/>
<project-select
v-if="groupId"
@@ -122,10 +123,10 @@ export default {
/>
<div class="clearfix prepend-top-10">
<button
+ ref="submit-button"
+ :disabled="disabled"
class="btn btn-success float-left"
type="submit"
- :disabled="disabled"
- ref="submit-button"
>
Submit issue
</button>
@@ -141,4 +142,3 @@ export default {
</div>
</div>
</template>
-
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index c4ee4f6c855..82fe6b0c5fb 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -21,8 +21,17 @@ window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.BoardSidebar = Vue.extend({
+ components: {
+ assigneeTitle,
+ assignees,
+ removeBtn: gl.issueBoards.RemoveIssueBtn,
+ subscriptions,
+ },
props: {
- currentUser: Object
+ currentUser: {
+ type: Object,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -64,6 +73,26 @@ gl.issueBoards.BoardSidebar = Vue.extend({
deep: true
},
},
+ created () {
+ // Get events from glDropdown
+ eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
+ eventHub.$on('sidebar.addAssignee', this.addAssignee);
+ eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
+ eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
+ },
+ beforeDestroy() {
+ eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
+ eventHub.$off('sidebar.addAssignee', this.addAssignee);
+ eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
+ eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
+ },
+ mounted () {
+ new IssuableContext(this.currentUser);
+ new MilestoneSelect();
+ new DueDateSelectors();
+ new LabelsSelect();
+ new Sidebar();
+ },
methods: {
closeSidebar () {
this.detail.issue = {};
@@ -97,30 +126,4 @@ gl.issueBoards.BoardSidebar = Vue.extend({
});
},
},
- created () {
- // Get events from glDropdown
- eventHub.$on('sidebar.removeAssignee', this.removeAssignee);
- eventHub.$on('sidebar.addAssignee', this.addAssignee);
- eventHub.$on('sidebar.removeAllAssignees', this.removeAllAssignees);
- eventHub.$on('sidebar.saveAssignees', this.saveAssignees);
- },
- beforeDestroy() {
- eventHub.$off('sidebar.removeAssignee', this.removeAssignee);
- eventHub.$off('sidebar.addAssignee', this.addAssignee);
- eventHub.$off('sidebar.removeAllAssignees', this.removeAllAssignees);
- eventHub.$off('sidebar.saveAssignees', this.saveAssignees);
- },
- mounted () {
- new IssuableContext(this.currentUser);
- new MilestoneSelect();
- new DueDateSelectors();
- new LabelsSelect();
- new Sidebar();
- },
- components: {
- assigneeTitle,
- assignees,
- removeBtn: gl.issueBoards.RemoveIssueBtn,
- subscriptions,
- },
});
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js
index dcc07810d01..f7d7b910e2f 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.js
+++ b/app/assets/javascripts/boards/components/issue_card_inner.js
@@ -9,6 +9,9 @@ window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
gl.issueBoards.IssueCardInner = Vue.extend({
+ components: {
+ UserAvatarLink,
+ },
props: {
issue: {
type: Object,
@@ -35,6 +38,7 @@ gl.issueBoards.IssueCardInner = Vue.extend({
groupId: {
type: Number,
required: false,
+ default: null,
},
},
data() {
@@ -44,9 +48,6 @@ gl.issueBoards.IssueCardInner = Vue.extend({
maxCounter: 99,
};
},
- components: {
- UserAvatarLink,
- },
computed: {
numberOverLimit() {
return this.issue.assignees.length - this.limitBeforeCounter;
diff --git a/app/assets/javascripts/boards/components/modal/empty_state.js b/app/assets/javascripts/boards/components/modal/empty_state.js
index 1e5f2383223..888bc9d7ef2 100644
--- a/app/assets/javascripts/boards/components/modal/empty_state.js
+++ b/app/assets/javascripts/boards/components/modal/empty_state.js
@@ -4,9 +4,6 @@ import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalEmptyState = Vue.extend({
mixins: [modalMixin],
- data() {
- return ModalStore.store;
- },
props: {
newIssuePath: {
type: String,
@@ -17,6 +14,9 @@ gl.issueBoards.ModalEmptyState = Vue.extend({
required: true,
},
},
+ data() {
+ return ModalStore.store;
+ },
computed: {
contents() {
const obj = {
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index 11bb3e98334..2745ca219ad 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -7,6 +7,9 @@ import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalFooter = Vue.extend({
+ components: {
+ 'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
+ },
mixins: [modalMixin],
data() {
return {
@@ -52,9 +55,6 @@ gl.issueBoards.ModalFooter = Vue.extend({
this.toggleModal(false);
},
},
- components: {
- 'lists-dropdown': gl.issueBoards.ModalFooterListsDropdown,
- },
template: `
<footer
class="form-actions add-issues-footer">
diff --git a/app/assets/javascripts/boards/components/modal/header.js b/app/assets/javascripts/boards/components/modal/header.js
index 67c29ebca72..5e511bb8935 100644
--- a/app/assets/javascripts/boards/components/modal/header.js
+++ b/app/assets/javascripts/boards/components/modal/header.js
@@ -5,6 +5,10 @@ import ModalStore from '../../stores/modal_store';
import modalMixin from '../../mixins/modal_mixins';
gl.issueBoards.ModalHeader = Vue.extend({
+ components: {
+ 'modal-tabs': gl.issueBoards.ModalTabs,
+ modalFilters,
+ },
mixins: [modalMixin],
props: {
projectId: {
@@ -42,10 +46,6 @@ gl.issueBoards.ModalHeader = Vue.extend({
ModalStore.toggleAll();
},
},
- components: {
- 'modal-tabs': gl.issueBoards.ModalTabs,
- modalFilters,
- },
template: `
<div>
<header class="add-issues-header form-actions">
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index 3083b3e4405..c8b2f45f177 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -10,6 +10,13 @@ import './empty_state';
import ModalStore from '../../stores/modal_store';
gl.issueBoards.IssuesModal = Vue.extend({
+ components: {
+ 'modal-header': gl.issueBoards.ModalHeader,
+ 'modal-list': gl.issueBoards.ModalList,
+ 'modal-footer': gl.issueBoards.ModalFooter,
+ 'empty-state': gl.issueBoards.ModalEmptyState,
+ loadingIcon,
+ },
props: {
newIssuePath: {
type: String,
@@ -43,6 +50,22 @@ gl.issueBoards.IssuesModal = Vue.extend({
data() {
return ModalStore.store;
},
+ computed: {
+ showList() {
+ if (this.activeTab === 'selected') {
+ return this.selectedIssues.length > 0;
+ }
+
+ return this.issuesCount > 0;
+ },
+ showEmptyState() {
+ if (!this.loading && this.issuesCount === 0) {
+ return true;
+ }
+
+ return this.activeTab === 'selected' && this.selectedIssues.length === 0;
+ },
+ },
watch: {
page() {
this.loadIssues();
@@ -80,6 +103,9 @@ gl.issueBoards.IssuesModal = Vue.extend({
deep: true,
},
},
+ created() {
+ this.page = 1;
+ },
methods: {
loadIssues(clearIssues = false) {
if (!this.showAddIssuesModal) return false;
@@ -112,32 +138,6 @@ gl.issueBoards.IssuesModal = Vue.extend({
});
},
},
- computed: {
- showList() {
- if (this.activeTab === 'selected') {
- return this.selectedIssues.length > 0;
- }
-
- return this.issuesCount > 0;
- },
- showEmptyState() {
- if (!this.loading && this.issuesCount === 0) {
- return true;
- }
-
- return this.activeTab === 'selected' && this.selectedIssues.length === 0;
- },
- },
- created() {
- this.page = 1;
- },
- components: {
- 'modal-header': gl.issueBoards.ModalHeader,
- 'modal-list': gl.issueBoards.ModalList,
- 'modal-footer': gl.issueBoards.ModalFooter,
- 'empty-state': gl.issueBoards.ModalEmptyState,
- loadingIcon,
- },
template: `
<div
class="add-issues-modal"
diff --git a/app/assets/javascripts/boards/components/modal/list.js b/app/assets/javascripts/boards/components/modal/list.js
index f86896d2178..11061c72a7b 100644
--- a/app/assets/javascripts/boards/components/modal/list.js
+++ b/app/assets/javascripts/boards/components/modal/list.js
@@ -3,6 +3,9 @@ import bp from '../../../breakpoints';
import ModalStore from '../../stores/modal_store';
gl.issueBoards.ModalList = Vue.extend({
+ components: {
+ 'issue-card-inner': gl.issueBoards.IssueCardInner,
+ },
props: {
issueLinkBase: {
type: String,
@@ -20,13 +23,6 @@ gl.issueBoards.ModalList = Vue.extend({
data() {
return ModalStore.store;
},
- watch: {
- activeTab() {
- if (this.activeTab === 'all') {
- ModalStore.purgeUnselectedIssues();
- }
- },
- },
computed: {
loopIssues() {
if (this.activeTab === 'all') {
@@ -50,6 +46,25 @@ gl.issueBoards.ModalList = Vue.extend({
return groups;
},
},
+ watch: {
+ activeTab() {
+ if (this.activeTab === 'all') {
+ ModalStore.purgeUnselectedIssues();
+ }
+ },
+ },
+ mounted() {
+ this.scrollHandlerWrapper = this.scrollHandler.bind(this);
+ this.setColumnCountWrapper = this.setColumnCount.bind(this);
+ this.setColumnCount();
+
+ this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
+ window.addEventListener('resize', this.setColumnCountWrapper);
+ },
+ beforeDestroy() {
+ this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
+ window.removeEventListener('resize', this.setColumnCountWrapper);
+ },
methods: {
scrollHandler() {
const currentPage = Math.floor(this.issues.length / this.perPage);
@@ -96,21 +111,6 @@ gl.issueBoards.ModalList = Vue.extend({
}
},
},
- mounted() {
- this.scrollHandlerWrapper = this.scrollHandler.bind(this);
- this.setColumnCountWrapper = this.setColumnCount.bind(this);
- this.setColumnCount();
-
- this.$refs.list.addEventListener('scroll', this.scrollHandlerWrapper);
- window.addEventListener('resize', this.setColumnCountWrapper);
- },
- beforeDestroy() {
- this.$refs.list.removeEventListener('scroll', this.scrollHandlerWrapper);
- window.removeEventListener('resize', this.setColumnCountWrapper);
- },
- components: {
- 'issue-card-inner': gl.issueBoards.IssueCardInner,
- },
template: `
<section
class="add-issues-list add-issues-list-columns"
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 71f49319c36..6dcd4aaec43 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -56,6 +56,7 @@ gl.issueBoards.newListDropdownInit = () => {
filterable: true,
selectable: true,
multiSelect: true,
+ containerSelector: '.js-tab-container-labels .dropdown-page-one .dropdown-content',
clicked (options) {
const { e } = options;
const label = options.selectedObj;
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 29ab13b8e0b..cdad8d238e3 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -7,6 +7,7 @@ import Vue from 'vue';
import Flash from '~/flash';
import { __ } from '~/locale';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
@@ -15,7 +16,6 @@ import './models/issue';
import './models/list';
import './models/milestone';
import './models/project';
-import './models/assignee';
import './stores/boards_store';
import ModalStore from './stores/modal_store';
import BoardService from './services/board_service';
diff --git a/app/assets/javascripts/boards/models/assignee.js b/app/assets/javascripts/boards/models/assignee.js
deleted file mode 100644
index 05dd449e4fd..00000000000
--- a/app/assets/javascripts/boards/models/assignee.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/* eslint-disable no-unused-vars */
-
-class ListAssignee {
- constructor(user, defaultAvatar) {
- this.id = user.id;
- this.name = user.name;
- this.username = user.username;
- this.avatar = user.avatar_url || defaultAvatar;
- }
-}
-
-window.ListAssignee = ListAssignee;
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 7144f4190e7..1f0fe7f9e85 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,12 +1,14 @@
/* eslint-disable space-before-function-paren, no-underscore-dangle, class-methods-use-this, consistent-return, no-shadow, no-param-reassign, max-len, no-unused-vars */
/* global ListIssue */
-/* global ListLabel */
+
+import ListLabel from '~/vue_shared/models/label';
+import ListAssignee from '~/vue_shared/models/assignee';
import queryData from '../utils/query_data';
const PER_PAGE = 20;
class List {
- constructor (obj, defaultAvatar) {
+ constructor(obj, defaultAvatar) {
this.id = obj.id;
this._uid = this.guid();
this.position = obj.position;
@@ -24,6 +26,9 @@ class List {
if (obj.label) {
this.label = new ListLabel(obj.label);
+ } else if (obj.user) {
+ this.assignee = new ListAssignee(obj.user);
+ this.title = this.assignee.name;
}
if (this.type !== 'blank' && this.id) {
@@ -34,14 +39,26 @@ class List {
}
guid() {
- const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
+ const s4 = () =>
+ Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
return `${s4()}${s4()}-${s4()}-${s4()}-${s4()}-${s4()}${s4()}${s4()}`;
}
- save () {
- return gl.boardService.createList(this.label.id)
+ save() {
+ const entity = this.label || this.assignee;
+ let entityType = '';
+ if (this.label) {
+ entityType = 'label_id';
+ } else {
+ entityType = 'assignee_id';
+ }
+
+ return gl.boardService
+ .createList(entity.id, entityType)
.then(res => res.data)
- .then((data) => {
+ .then(data => {
this.id = data.id;
this.type = data.list_type;
this.position = data.position;
@@ -50,25 +67,23 @@ class List {
});
}
- destroy () {
+ destroy() {
const index = gl.issueBoards.BoardsStore.state.lists.indexOf(this);
gl.issueBoards.BoardsStore.state.lists.splice(index, 1);
gl.issueBoards.BoardsStore.updateNewListDropdown(this.id);
- gl.boardService.destroyList(this.id)
- .catch(() => {
- // TODO: handle request error
- });
+ gl.boardService.destroyList(this.id).catch(() => {
+ // TODO: handle request error
+ });
}
- update () {
- gl.boardService.updateList(this.id, this.position)
- .catch(() => {
- // TODO: handle request error
- });
+ update() {
+ gl.boardService.updateList(this.id, this.position).catch(() => {
+ // TODO: handle request error
+ });
}
- nextPage () {
+ nextPage() {
if (this.issuesSize > this.issues.length) {
if (this.issues.length / PER_PAGE >= 1) {
this.page += 1;
@@ -78,7 +93,7 @@ class List {
}
}
- getIssues (emptyIssues = true) {
+ getIssues(emptyIssues = true) {
const data = queryData(gl.issueBoards.BoardsStore.filter.path, { page: this.page });
if (this.label && data.label_name) {
@@ -89,7 +104,8 @@ class List {
this.loading = true;
}
- return gl.boardService.getIssuesForList(this.id, data)
+ return gl.boardService
+ .getIssuesForList(this.id, data)
.then(res => res.data)
.then((data) => {
this.loading = false;
@@ -103,11 +119,12 @@ class List {
});
}
- newIssue (issue) {
+ newIssue(issue) {
this.addIssue(issue, null, 0);
this.issuesSize += 1;
- return gl.boardService.newIssue(this.id, issue)
+ return gl.boardService
+ .newIssue(this.id, issue)
.then(res => res.data)
.then((data) => {
issue.id = data.id;
@@ -123,13 +140,13 @@ class List {
});
}
- createIssues (data) {
- data.forEach((issueObj) => {
+ createIssues(data) {
+ data.forEach(issueObj => {
this.addIssue(new ListIssue(issueObj, this.defaultAvatar));
});
}
- addIssue (issue, listFrom, newIndex) {
+ addIssue(issue, listFrom, newIndex) {
let moveBeforeId = null;
let moveAfterId = null;
@@ -152,6 +169,13 @@ class List {
issue.addLabel(this.label);
}
+ if (this.assignee) {
+ if (listFrom && listFrom.type === 'assignee') {
+ issue.removeAssignee(listFrom.assignee);
+ }
+ issue.addAssignee(this.assignee);
+ }
+
if (listFrom) {
this.issuesSize += 1;
@@ -160,29 +184,29 @@ class List {
}
}
- moveIssue (issue, oldIndex, newIndex, moveBeforeId, moveAfterId) {
+ moveIssue(issue, oldIndex, newIndex, moveBeforeId, moveAfterId) {
this.issues.splice(oldIndex, 1);
this.issues.splice(newIndex, 0, issue);
- gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId)
- .catch(() => {
- // TODO: handle request error
- });
+ gl.boardService.moveIssue(issue.id, null, null, moveBeforeId, moveAfterId).catch(() => {
+ // TODO: handle request error
+ });
}
updateIssueLabel(issue, listFrom, moveBeforeId, moveAfterId) {
- gl.boardService.moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
+ gl.boardService
+ .moveIssue(issue.id, listFrom.id, this.id, moveBeforeId, moveAfterId)
.catch(() => {
// TODO: handle request error
});
}
- findIssue (id) {
+ findIssue(id) {
return this.issues.find(issue => issue.id === id);
}
- removeIssue (removeIssue) {
- this.issues = this.issues.filter((issue) => {
+ removeIssue(removeIssue) {
+ this.issues = this.issues.filter(issue => {
const matchesRemove = removeIssue.id === issue.id;
if (matchesRemove) {
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 7c90597f77c..029b0971f2c 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -30,11 +30,13 @@ export default class BoardService {
return axios.post(this.listsEndpointGenerate, {});
}
- createList(labelId) {
+ createList(entityId, entityType) {
+ const list = {
+ [entityType]: entityId,
+ };
+
return axios.post(this.listsEndpoint, {
- list: {
- label_id: labelId,
- },
+ list,
});
}
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 20e78edf2a2..7dc83843e9b 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -103,8 +103,15 @@ gl.issueBoards.BoardsStore = {
const listLabels = issueLists.map(listIssue => listIssue.label);
if (!issueTo) {
- // Add to new lists issues if it doesn't already exist
- listTo.addIssue(issue, listFrom, newIndex);
+ // Check if target list assignee is already present in this issue
+ if ((listTo.type === 'assignee' && listFrom.type === 'assignee') &&
+ issue.findAssignee(listTo.assignee)) {
+ const targetIssue = listTo.findIssue(issue.id);
+ targetIssue.removeAssignee(listFrom.assignee);
+ } else {
+ // Add to new lists issues if it doesn't already exist
+ listTo.addIssue(issue, listFrom, newIndex);
+ }
} else {
listTo.updateIssueLabel(issue, listFrom);
issueTo.removeLabel(listFrom.label);
@@ -115,7 +122,11 @@ gl.issueBoards.BoardsStore = {
list.removeIssue(issue);
});
issue.removeLabels(listLabels);
- } else {
+ } else if (listTo.type === 'backlog' && listFrom.type === 'assignee') {
+ issue.removeAssignee(listFrom.assignee);
+ listFrom.removeIssue(issue);
+ } else if ((listTo.type !== 'label' && listFrom.type === 'assignee') ||
+ (listTo.type !== 'assignee' && listFrom.type === 'label')) {
listFrom.removeIssue(issue);
}
},
@@ -126,11 +137,12 @@ gl.issueBoards.BoardsStore = {
list.moveIssue(issue, oldIndex, newIndex, beforeId, afterId);
},
findList (key, val, type = 'label') {
- return this.state.lists.filter((list) => {
- const byType = type ? list['type'] === type : true;
+ const filteredList = this.state.lists.filter((list) => {
+ const byType = type ? (list.type === type) || (list.type === 'assignee') : true;
return list[key] === val && byType;
- })[0];
+ });
+ return filteredList[0];
},
updateFiltersUrl () {
history.pushState(null, null, `?${this.filter.path}`);
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 98c0b9c22a8..ec52fdfdf32 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -125,8 +125,8 @@
<template>
<div
- class="gl-responsive-table-row gl-responsive-table-row-col-span"
:class="rowJsClass"
+ class="gl-responsive-table-row gl-responsive-table-row-col-span"
>
<div
class="gl-responsive-table-row-layout"
@@ -155,8 +155,8 @@
<slot name="description"></slot>
</div>
<div
- class="table-section table-button-footer section-align-top"
:class="{ 'section-20': showManageButton, 'section-15': !showManageButton }"
+ class="table-section table-button-footer section-align-top"
role="gridcell"
>
<div
@@ -164,18 +164,18 @@
class="btn-group table-action-buttons"
>
<a
- class="btn"
:href="manageLink"
+ class="btn"
>
{{ manageButtonLabel }}
</a>
</div>
<div class="btn-group table-action-buttons">
<loading-button
- class="js-cluster-application-install-button"
:loading="installButtonLoading"
:disabled="installButtonDisabled"
:label="installButtonLabel"
+ class="js-cluster-application-install-button"
@click="installClicked"
/>
</div>
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 9d6be555a2c..8ee7279e544 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -152,11 +152,11 @@ export default {
<application-row
id="helm"
:title="applications.helm.title"
- title-link="https://docs.helm.sh/"
:status="applications.helm.status"
:status-reason="applications.helm.statusReason"
:request-status="applications.helm.requestStatus"
:request-reason="applications.helm.requestReason"
+ title-link="https://docs.helm.sh/"
>
<div slot="description">
{{ s__(`ClusterIntegration|Helm streamlines installing
@@ -168,11 +168,11 @@ export default {
<application-row
:id="ingressId"
:title="applications.ingress.title"
- title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
:status="applications.ingress.status"
:status-reason="applications.ingress.statusReason"
:request-status="applications.ingress.requestStatus"
:request-reason="applications.ingress.requestReason"
+ title-link="https://kubernetes.io/docs/concepts/services-networking/ingress/"
>
<div slot="description">
<p>
@@ -191,10 +191,10 @@ export default {
class="input-group"
>
<input
- type="text"
id="ingress-ip-address"
- class="form-control js-ip-address"
:value="ingressExternalIp"
+ type="text"
+ class="form-control js-ip-address"
readonly
/>
<span class="input-group-append">
@@ -255,12 +255,12 @@ export default {
<application-row
id="prometheus"
:title="applications.prometheus.title"
- title-link="https://prometheus.io/docs/introduction/overview/"
:manage-link="managePrometheusPath"
:status="applications.prometheus.status"
:status-reason="applications.prometheus.statusReason"
:request-status="applications.prometheus.requestStatus"
:request-reason="applications.prometheus.requestReason"
+ title-link="https://prometheus.io/docs/introduction/overview/"
>
<div
slot="description"
@@ -271,11 +271,11 @@ export default {
<application-row
id="runner"
:title="applications.runner.title"
- title-link="https://docs.gitlab.com/runner/"
:status="applications.runner.status"
:status-reason="applications.runner.statusReason"
:request-status="applications.runner.requestStatus"
:request-reason="applications.runner.requestReason"
+ title-link="https://docs.gitlab.com/runner/"
>
<div slot="description">
{{ s__(`ClusterIntegration|GitLab Runner connects to this
@@ -287,12 +287,12 @@ export default {
<application-row
id="jupyter"
:title="applications.jupyter.title"
- title-link="https://jupyterhub.readthedocs.io/en/stable/"
:status="applications.jupyter.status"
:status-reason="applications.jupyter.statusReason"
:request-status="applications.jupyter.requestStatus"
:request-reason="applications.jupyter.requestReason"
:install-application-request-params="{ hostname: applications.jupyter.hostname }"
+ title-link="https://jupyterhub.readthedocs.io/en/stable/"
>
<div slot="description">
<p>
@@ -311,10 +311,10 @@ export default {
<div class="input-group">
<input
- type="text"
- class="form-control js-hostname"
v-model="applications.jupyter.hostname"
:readonly="jupyterInstalled"
+ type="text"
+ class="form-control js-hostname"
/>
<span
class="input-group-btn"
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index 3a4ac09f67c..d90db7b103c 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -95,7 +95,7 @@ export default class ClusterStore {
this.state.applications.jupyter.hostname =
serverAppEntry.hostname ||
(this.state.applications.ingress.externalIp
- ? `jupyter.${this.state.applications.ingress.externalIp}.xip.io`
+ ? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: '');
}
});
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.vue b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
index 24d63b99a29..95c4be64d35 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.vue
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.vue
@@ -77,9 +77,9 @@
<div class="content-list pipelines">
<loading-icon
+ v-if="isLoading"
:label="s__('Pipelines|Loading Pipelines')"
size="3"
- v-if="isLoading"
class="prepend-top-20"
/>
@@ -91,8 +91,8 @@
/>
<div
- class="table-holder"
v-else-if="shouldRenderTable"
+ class="table-holder"
>
<pipelines-table-component
:pipelines="state.pipelines"
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index 09d490106df..108082799ef 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -66,8 +66,14 @@ export default class CreateMergeRequestDropdown {
}
bindEvents() {
- this.createMergeRequestButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
- this.createTargetButton.addEventListener('click', this.onClickCreateMergeRequestButton.bind(this));
+ this.createMergeRequestButton.addEventListener(
+ 'click',
+ this.onClickCreateMergeRequestButton.bind(this),
+ );
+ this.createTargetButton.addEventListener(
+ 'click',
+ this.onClickCreateMergeRequestButton.bind(this),
+ );
this.branchInput.addEventListener('keyup', this.onChangeInput.bind(this));
this.dropdownToggle.addEventListener('click', this.onClickSetFocusOnBranchNameInput.bind(this));
this.refInput.addEventListener('keyup', this.onChangeInput.bind(this));
@@ -77,7 +83,8 @@ export default class CreateMergeRequestDropdown {
checkAbilityToCreateBranch() {
this.setUnavailableButtonState();
- axios.get(this.canCreatePath)
+ axios
+ .get(this.canCreatePath)
.then(({ data }) => {
this.setUnavailableButtonState(false);
@@ -105,7 +112,8 @@ export default class CreateMergeRequestDropdown {
createBranch() {
this.isCreatingBranch = true;
- return axios.post(this.createBranchPath)
+ return axios
+ .post(this.createBranchPath)
.then(({ data }) => {
this.branchCreated = true;
window.location.href = data.url;
@@ -116,7 +124,8 @@ export default class CreateMergeRequestDropdown {
createMergeRequest() {
this.isCreatingMergeRequest = true;
- return axios.post(this.createMrPath)
+ return axios
+ .post(this.createMrPath)
.then(({ data }) => {
this.mergeRequestCreated = true;
window.location.href = data.url;
@@ -195,7 +204,8 @@ export default class CreateMergeRequestDropdown {
getRef(ref, target = 'all') {
if (!ref) return false;
- return axios.get(this.refsPath + ref)
+ return axios
+ .get(`${this.refsPath}${encodeURIComponent(ref)}`)
.then(({ data }) => {
const branches = data[Object.keys(data)[0]];
const tags = data[Object.keys(data)[1]];
@@ -204,7 +214,8 @@ export default class CreateMergeRequestDropdown {
if (target === 'branch') {
result = CreateMergeRequestDropdown.findByValue(branches, ref);
} else {
- result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
+ result =
+ CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
CreateMergeRequestDropdown.findByValue(tags, ref, true);
this.suggestedRef = result;
}
@@ -255,11 +266,13 @@ export default class CreateMergeRequestDropdown {
}
isBusy() {
- return this.isCreatingMergeRequest ||
+ return (
+ this.isCreatingMergeRequest ||
this.mergeRequestCreated ||
this.isCreatingBranch ||
this.branchCreated ||
- this.isGettingRef;
+ this.isGettingRef
+ );
}
onChangeInput(event) {
@@ -271,7 +284,8 @@ export default class CreateMergeRequestDropdown {
value = this.branchInput.value;
} else if (event.target === this.refInput) {
target = 'ref';
- value = event.target.value.slice(0, event.target.selectionStart) +
+ value =
+ event.target.value.slice(0, event.target.selectionStart) +
event.target.value.slice(event.target.selectionEnd);
} else {
return false;
@@ -396,7 +410,8 @@ export default class CreateMergeRequestDropdown {
showNotAvailableMessage(target) {
const { input, message } = this.getTargetData(target);
- const text = target === 'branch' ? __('Branch is already taken') : __('Source is not available');
+ const text =
+ target === 'branch' ? __('Branch is already taken') : __('Source is not available');
this.removeMessage(target);
input.classList.add('gl-field-error-outline');
@@ -459,11 +474,15 @@ export default class CreateMergeRequestDropdown {
// target - 'branch' or 'ref'
// ref - string - the new value to use as branch or ref
updateCreatePaths(target, ref) {
- const pathReplacement = `$1${ref}`;
+ const pathReplacement = `$1${encodeURIComponent(ref)}`;
- this.createBranchPath = this.createBranchPath.replace(this.regexps[target].createBranchPath,
- pathReplacement);
- this.createMrPath = this.createMrPath.replace(this.regexps[target].createMrPath,
- pathReplacement);
+ this.createBranchPath = this.createBranchPath.replace(
+ this.regexps[target].createBranchPath,
+ pathReplacement,
+ );
+ this.createMrPath = this.createMrPath.replace(
+ this.regexps[target].createMrPath,
+ pathReplacement,
+ );
}
}
diff --git a/app/assets/javascripts/cycle_analytics/components/banner.vue b/app/assets/javascripts/cycle_analytics/components/banner.vue
index 3204b8dd8e7..410d4873e55 100644
--- a/app/assets/javascripts/cycle_analytics/components/banner.vue
+++ b/app/assets/javascripts/cycle_analytics/components/banner.vue
@@ -23,9 +23,9 @@
<template>
<div class="landing content-block">
<button
+ :aria-label="__('Dismiss Cycle Analytics introduction box')"
class="js-ca-dismiss-button dismiss-button"
type="button"
- :aria-label="__('Dismiss Cycle Analytics introduction box')"
@click="dismissOverviewDialog"
>
<i
diff --git a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
index 5be17081b58..b626b187651 100644
--- a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.vue
@@ -19,14 +19,14 @@
class="events-info float-right"
>
<i
- class="fa fa-warning"
v-tooltip
- aria-hidden="true"
:title="n__(
'Limited to showing %d event at most',
'Limited to showing %d events at most',
50
)"
+ class="fa fa-warning"
+ aria-hidden="true"
data-placement="top"
>
</i>
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_component.vue
index 907638d798a..312fe75dde4 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_component.vue
@@ -38,8 +38,8 @@
<user-avatar-image :img-src="issue.author.avatarUrl"/>
<h5 class="item-title issue-title">
<a
- class="issue-title"
:href="issue.url"
+ class="issue-title"
>
{{ issue.title }}
</a>
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 34aa04083e6..d4735d030fc 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.vue
@@ -74,12 +74,12 @@
</template>
<template v-else>
<span
- class="merge-request-branch"
v-if="mergeRequest.branch"
+ class="merge-request-branch"
>
<icon
- name="fork"
:size="16"
+ name="fork"
/>
<a :href="mergeRequest.branch.url">
{{ mergeRequest.branch.name }}
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
index 92f2a95a66a..22637485c01 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.vue
@@ -38,8 +38,8 @@
<ul class="stage-event-list">
<li
v-for="(build, i) in items"
- class="stage-event-item item-build-component"
:key="i"
+ class="stage-event-item item-build-component"
>
<div class="item-details">
<!-- FIXME: Pass an alt attribute here for accessibility -->
@@ -52,8 +52,8 @@
#{{ build.id }}
</a>
<icon
- name="fork"
:size="16"
+ name="fork"
/>
<a
:href="build.branch.url"
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
index b84bb6ed792..a0796f299e7 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
+++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.vue
@@ -64,8 +64,8 @@
#{{ build.id }}
</a>
<icon
- name="fork"
:size="16"
+ name="fork"
/>
<a
:href="build.branch.url"
diff --git a/app/assets/javascripts/deploy_keys/components/action_btn.vue b/app/assets/javascripts/deploy_keys/components/action_btn.vue
index 67dda0e29cb..7399fc97d45 100644
--- a/app/assets/javascripts/deploy_keys/components/action_btn.vue
+++ b/app/assets/javascripts/deploy_keys/components/action_btn.vue
@@ -40,9 +40,9 @@ export default {
<template>
<button
- class="btn"
:class="[{ disabled: isLoading }, btnCssClass]"
:disabled="isLoading"
+ class="btn"
@click="doAction">
<slot></slot>
<loading-icon
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index c41fe55db63..2cfa13fdc75 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -116,8 +116,8 @@ export default {
<div class="append-bottom-default deploy-keys">
<loading-icon
v-if="isLoading && !hasKeys"
- size="2"
:label="s__('DeployKeys|Loading deploy keys')"
+ size="2"
/>
<template v-else-if="hasKeys">
<div class="top-area scrolling-tabs-container inner-page-scroll-tabs">
@@ -138,16 +138,16 @@ export default {
<navigation-tabs
:tabs="tabs"
- @onChangeTab="onChangeTab"
scope="deployKeys"
+ @onChangeTab="onChangeTab"
/>
</div>
<keys-panel
- class="qa-project-deploy-keys"
:project-id="projectId"
:keys="keys[currentTab]"
:store="store"
:endpoint="endpoint"
+ class="qa-project-deploy-keys"
/>
</template>
</div>
diff --git a/app/assets/javascripts/deploy_keys/components/key.vue b/app/assets/javascripts/deploy_keys/components/key.vue
index 6c2af7fa768..f66ca070445 100644
--- a/app/assets/javascripts/deploy_keys/components/key.vue
+++ b/app/assets/javascripts/deploy_keys/components/key.vue
@@ -135,9 +135,9 @@ export default {
<div class="table-mobile-content deploy-project-list">
<template v-if="projects.length > 0">
<a
- class="label deploy-project-label"
- :title="projectTooltipTitle(firstProject)"
v-tooltip
+ :title="projectTooltipTitle(firstProject)"
+ class="label deploy-project-label"
>
<span>
{{ firstProject.project.full_name }}
@@ -145,22 +145,22 @@ export default {
<icon :name="firstProject.can_push ? 'lock-open' : 'lock'"/>
</a>
<a
+ v-tooltip
v-if="isExpandable"
+ :title="restProjectsTooltip"
class="label deploy-project-label"
@click="toggleExpanded"
- :title="restProjectsTooltip"
- v-tooltip
>
<span>{{ restProjectsLabel }}</span>
</a>
<a
- v-else-if="isExpanded"
+ v-tooltip
v-for="deployKeysProject in restProjects"
+ v-else-if="isExpanded"
:key="deployKeysProject.project.full_path"
- class="label deploy-project-label"
:href="deployKeysProject.project.full_path"
:title="projectTooltipTitle(deployKeysProject)"
- v-tooltip
+ class="label deploy-project-label"
>
<span>
{{ deployKeysProject.project.full_name }}
@@ -181,8 +181,8 @@ export default {
</div>
<div class="table-mobile-content text-secondary key-created-at">
<span
- :title="tooltipTitle(deployKey.created_at)"
- v-tooltip>
+ v-tooltip
+ :title="tooltipTitle(deployKey.created_at)">
<icon name="calendar"/>
<span>{{ timeFormated(deployKey.created_at) }}</span>
</span>
@@ -198,34 +198,34 @@ export default {
{{ __('Enable') }}
</action-btn>
<a
+ v-tooltip
v-if="deployKey.can_edit"
- class="btn btn-default text-secondary"
:href="editDeployKeyPath"
:title="__('Edit')"
+ class="btn btn-default text-secondary"
data-container="body"
- v-tooltip
>
<icon name="pencil"/>
</a>
<action-btn
+ v-tooltip
v-if="isRemovable"
:deploy-key="deployKey"
+ :title="__('Remove')"
btn-css-class="btn-danger"
type="remove"
- :title="__('Remove')"
data-container="body"
- v-tooltip
>
<icon name="remove"/>
</action-btn>
<action-btn
+ v-tooltip
v-else-if="isEnabled"
:deploy-key="deployKey"
+ :title="__('Disable')"
btn-css-class="btn-warning"
type="disable"
- :title="__('Disable')"
data-container="body"
- v-tooltip
>
<icon name="cancel"/>
</action-btn>
diff --git a/app/assets/javascripts/deploy_keys/components/keys_panel.vue b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
index 3b146c7389a..2f057ca29f6 100644
--- a/app/assets/javascripts/deploy_keys/components/keys_panel.vue
+++ b/app/assets/javascripts/deploy_keys/components/keys_panel.vue
@@ -59,8 +59,8 @@ export default {
/>
</template>
<div
- class="settings-message text-center"
v-else
+ class="settings-message text-center"
>
{{ s__('DeployKeys|No deploy keys found. Create one with the form above.') }}
</div>
diff --git a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
index d1260ff5373..ed24d1775f4 100644
--- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
@@ -6,7 +6,10 @@ import Vue from 'vue';
const CommentAndResolveBtn = Vue.extend({
props: {
- discussionId: String,
+ discussionId: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
diff --git a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
index fe9b0795609..40f7c2fe5f3 100644
--- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
+++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
@@ -7,7 +7,15 @@ import Notes from '../../notes';
import userAvatarImage from '../../vue_shared/components/user_avatar/user_avatar_image.vue';
const DiffNoteAvatars = Vue.extend({
- props: ['discussionId'],
+ components: {
+ userAvatarImage,
+ },
+ props: {
+ discussionId: {
+ type: String,
+ required: true,
+ },
+ },
data() {
return {
isVisible: false,
@@ -17,77 +25,6 @@ const DiffNoteAvatars = Vue.extend({
collapseIcon,
};
},
- components: {
- userAvatarImage,
- },
- template: `
- <div class="diff-comment-avatar-holders"
- :class="discussionClassName"
- v-show="notesCount !== 0">
- <div v-if="!isVisible">
- <!-- FIXME: Pass an alt attribute here for accessibility -->
- <user-avatar-image
- v-for="note in notesSubset"
- :key="note.id"
- class="diff-comment-avatar js-diff-comment-avatar"
- @click.native="clickedAvatar($event)"
- :img-src="note.authorAvatar"
- :tooltip-text="getTooltipText(note)"
- :data-line-type="lineType"
- :size="19"
- data-html="true"
- />
- <span v-if="notesCount > shownAvatars"
- class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
- data-container="body"
- data-placement="top"
- ref="extraComments"
- role="button"
- :data-line-type="lineType"
- :title="extraNotesTitle"
- @click="clickedAvatar($event)">{{ moreText }}</span>
- </div>
- <button class="diff-notes-collapse js-diff-comment-avatar"
- type="button"
- aria-label="Show comments"
- :data-line-type="lineType"
- @click="clickedAvatar($event)"
- v-if="isVisible"
- v-html="collapseIcon">
- </button>
- </div>
- `,
- mounted() {
- this.$nextTick(() => {
- this.addNoCommentClass();
- this.setDiscussionVisible();
-
- this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
- });
-
- $(document).on('toggle.comments', () => {
- this.$nextTick(() => {
- this.setDiscussionVisible();
- });
- });
- },
- beforeDestroy() {
- this.addNoCommentClass();
- $(document).off('toggle.comments');
- },
- watch: {
- storeState: {
- handler() {
- this.$nextTick(() => {
- $('.has-tooltip', this.$el).tooltip('_fixTitle');
-
- // We need to add/remove a class to an element that is outside the Vue instance
- this.addNoCommentClass();
- });
- },
- deep: true,
- },
- },
computed: {
discussionClassName() {
return `js-diff-avatars-${this.discussionId}`;
@@ -128,6 +65,37 @@ const DiffNoteAvatars = Vue.extend({
return `${plusSign}${this.notesCount - this.shownAvatars}`;
},
},
+ watch: {
+ storeState: {
+ handler() {
+ this.$nextTick(() => {
+ $('.has-tooltip', this.$el).tooltip('_fixTitle');
+
+ // We need to add/remove a class to an element that is outside the Vue instance
+ this.addNoCommentClass();
+ });
+ },
+ deep: true,
+ },
+ },
+ mounted() {
+ this.$nextTick(() => {
+ this.addNoCommentClass();
+ this.setDiscussionVisible();
+
+ this.lineType = $(this.$el).closest('.diff-line-num').hasClass('old_line') ? 'old' : 'new';
+ });
+
+ $(document).on('toggle.comments', () => {
+ this.$nextTick(() => {
+ this.setDiscussionVisible();
+ });
+ });
+ },
+ beforeDestroy() {
+ this.addNoCommentClass();
+ $(document).off('toggle.comments');
+ },
methods: {
clickedAvatar(e) {
Notes.instance.onAddDiffNote(e);
@@ -164,6 +132,43 @@ const DiffNoteAvatars = Vue.extend({
return `${note.authorName}: ${note.noteTruncated}`;
},
},
+ template: `
+ <div class="diff-comment-avatar-holders"
+ :class="discussionClassName"
+ v-show="notesCount !== 0">
+ <div v-if="!isVisible">
+ <!-- FIXME: Pass an alt attribute here for accessibility -->
+ <user-avatar-image
+ v-for="note in notesSubset"
+ :key="note.id"
+ class="diff-comment-avatar js-diff-comment-avatar"
+ @click.native="clickedAvatar($event)"
+ :img-src="note.authorAvatar"
+ :tooltip-text="getTooltipText(note)"
+ :data-line-type="lineType"
+ :size="19"
+ data-html="true"
+ />
+ <span v-if="notesCount > shownAvatars"
+ class="diff-comments-more-count has-tooltip js-diff-comment-avatar"
+ data-container="body"
+ data-placement="top"
+ ref="extraComments"
+ role="button"
+ :data-line-type="lineType"
+ :title="extraNotesTitle"
+ @click="clickedAvatar($event)">{{ moreText }}</span>
+ </div>
+ <button class="diff-notes-collapse js-diff-comment-avatar"
+ type="button"
+ aria-label="Show comments"
+ :data-line-type="lineType"
+ @click="clickedAvatar($event)"
+ v-if="isVisible"
+ v-html="collapseIcon">
+ </button>
+ </div>
+ `,
});
Vue.component('diff-note-avatars', DiffNoteAvatars);
diff --git a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
index 8f9186dfb9a..2ce4b050763 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
@@ -10,7 +10,10 @@ import '../mixins/discussion';
const JumpToDiscussion = Vue.extend({
mixins: [DiscussionMixins],
props: {
- discussionId: String
+ discussionId: {
+ type: String,
+ required: true,
+ },
},
data: function () {
return {
@@ -52,6 +55,9 @@ const JumpToDiscussion = Vue.extend({
return lastId;
}
},
+ created() {
+ this.discussion = this.discussions[this.discussionId];
+ },
methods: {
jumpToNextUnresolvedDiscussion: function () {
let discussionsSelector;
@@ -202,9 +208,6 @@ const JumpToDiscussion = Vue.extend({
});
}
},
- created() {
- this.discussion = this.discussions[this.discussionId];
- },
});
Vue.component('jump-to-discussion', JumpToDiscussion);
diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js b/app/assets/javascripts/diff_notes/components/resolve_btn.js
index 8d66417abac..07f3be29090 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js
@@ -8,14 +8,38 @@ import Flash from '../../flash';
const ResolveBtn = Vue.extend({
props: {
- noteId: Number,
- discussionId: String,
- resolved: Boolean,
- canResolve: Boolean,
- resolvedBy: String,
- authorName: String,
- authorAvatar: String,
- noteTruncated: String,
+ noteId: {
+ type: Number,
+ required: true,
+ },
+ discussionId: {
+ type: String,
+ required: true,
+ },
+ resolved: {
+ type: Boolean,
+ required: true,
+ },
+ canResolve: {
+ type: Boolean,
+ required: true,
+ },
+ resolvedBy: {
+ type: String,
+ required: true,
+ },
+ authorName: {
+ type: String,
+ required: true,
+ },
+ authorAvatar: {
+ type: String,
+ required: true,
+ },
+ noteTruncated: {
+ type: String,
+ required: true,
+ },
},
data: function () {
return {
@@ -23,12 +47,6 @@ const ResolveBtn = Vue.extend({
loading: false
};
},
- watch: {
- 'discussions': {
- handler: 'updateTooltip',
- deep: true
- }
- },
computed: {
discussion: function () {
return this.discussions[this.discussionId];
@@ -56,6 +74,32 @@ const ResolveBtn = Vue.extend({
return this.note.resolved_by;
},
},
+ watch: {
+ 'discussions': {
+ handler: 'updateTooltip',
+ deep: true
+ }
+ },
+ mounted: function () {
+ $(this.$refs.button).tooltip({
+ container: 'body'
+ });
+ },
+ beforeDestroy: function () {
+ CommentsStore.delete(this.discussionId, this.noteId);
+ },
+ created: function () {
+ CommentsStore.create({
+ discussionId: this.discussionId,
+ noteId: this.noteId,
+ canResolve: this.canResolve,
+ resolved: this.resolved,
+ resolvedBy: this.resolvedBy,
+ authorName: this.authorName,
+ authorAvatar: this.authorAvatar,
+ noteTruncated: this.noteTruncated,
+ });
+ },
methods: {
updateTooltip: function () {
this.$nextTick(() => {
@@ -95,26 +139,6 @@ const ResolveBtn = Vue.extend({
.catch(() => new Flash('An error occurred when trying to resolve a comment. Please try again.'));
}
},
- mounted: function () {
- $(this.$refs.button).tooltip({
- container: 'body'
- });
- },
- beforeDestroy: function () {
- CommentsStore.delete(this.discussionId, this.noteId);
- },
- created: function () {
- CommentsStore.create({
- discussionId: this.discussionId,
- noteId: this.noteId,
- canResolve: this.canResolve,
- resolved: this.resolved,
- resolvedBy: this.resolvedBy,
- authorName: this.authorName,
- authorAvatar: this.authorAvatar,
- noteTruncated: this.noteTruncated,
- });
- }
});
Vue.component('resolve-btn', ResolveBtn);
diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js b/app/assets/javascripts/diff_notes/components/resolve_count.js
index fe7cf8f5fc1..9f613410e81 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js
@@ -9,7 +9,10 @@ import '../mixins/discussion';
window.ResolveCount = Vue.extend({
mixins: [DiscussionMixins],
props: {
- loggedOut: Boolean
+ loggedOut: {
+ type: Boolean,
+ required: true,
+ },
},
data: function () {
return {
diff --git a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
index 6a036e96171..210a00c5fc2 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
@@ -6,9 +6,18 @@ import Vue from 'vue';
const ResolveDiscussionBtn = Vue.extend({
props: {
- discussionId: String,
- mergeRequestId: Number,
- canResolve: Boolean,
+ discussionId: {
+ type: String,
+ required: true,
+ },
+ mergeRequestId: {
+ type: Number,
+ required: true,
+ },
+ canResolve: {
+ type: Boolean,
+ required: true,
+ },
},
data: function() {
return {
@@ -45,16 +54,16 @@ const ResolveDiscussionBtn = Vue.extend({
}
}
},
+ created: function () {
+ CommentsStore.createDiscussion(this.discussionId, this.canResolve);
+
+ this.discussion = CommentsStore.state[this.discussionId];
+ },
methods: {
resolve: function () {
ResolveService.toggleResolveForDiscussion(this.mergeRequestId, this.discussionId);
}
},
- created: function () {
- CommentsStore.createDiscussion(this.discussionId, this.canResolve);
-
- this.discussion = CommentsStore.state[this.discussionId];
- }
});
Vue.component('resolve-discussion-btn', ResolveDiscussionBtn);
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
index e17daec6a92..d5161ab7df9 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
@@ -69,9 +69,10 @@ export default () => {
gl.diffNotesCompileComponents();
- if (!hasVueMRDiscussionsCookie()) {
+ const resolveCountAppEl = document.querySelector('#resolve-count-app');
+ if (!hasVueMRDiscussionsCookie() && resolveCountAppEl) {
new Vue({
- el: '#resolve-count-app',
+ el: resolveCountAppEl,
components: {
'resolve-count': ResolveCount
},
diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue
index 6bd7c6b49cb..9aa224fa407 100644
--- a/app/assets/javascripts/environments/components/container.vue
+++ b/app/assets/javascripts/environments/components/container.vue
@@ -43,17 +43,17 @@
<div class="environments-container">
<loading-icon
+ v-if="isLoading"
class="prepend-top-default"
label="Loading environments"
- v-if="isLoading"
size="3"
/>
<slot name="emptyState"></slot>
<div
- class="table-holder"
- v-if="!isLoading && environments.length > 0">
+ v-if="!isLoading && environments.length > 0"
+ class="table-holder">
<environment-table
:environments="environments"
diff --git a/app/assets/javascripts/environments/components/environment_actions.vue b/app/assets/javascripts/environments/components/environment_actions.vue
index 0b3fef9fcca..e3652fe739e 100644
--- a/app/assets/javascripts/environments/components/environment_actions.vue
+++ b/app/assets/javascripts/environments/components/environment_actions.vue
@@ -52,18 +52,18 @@
role="group">
<button
v-tooltip
+ :title="title"
+ :aria-label="title"
+ :disabled="isLoading"
type="button"
class="dropdown btn btn-default dropdown-new js-dropdown-play-icon-container"
data-container="body"
data-toggle="dropdown"
- :title="title"
- :aria-label="title"
- :disabled="isLoading"
>
<span>
<icon
- name="play"
:size="12"
+ name="play"
/>
<i
class="fa fa-caret-down"
@@ -79,15 +79,15 @@
v-for="(action, i) in actions"
:key="i">
<button
+ :class="{ disabled: isActionDisabled(action) }"
+ :disabled="isActionDisabled(action)"
type="button"
class="js-manual-action-link no-btn btn"
@click="onClickAction(action.play_path)"
- :class="{ disabled: isActionDisabled(action) }"
- :disabled="isActionDisabled(action)"
>
<icon
- name="play"
:size="12"
+ name="play"
/>
<span>
{{ action.name }}
diff --git a/app/assets/javascripts/environments/components/environment_external_url.vue b/app/assets/javascripts/environments/components/environment_external_url.vue
index ea6f1168c68..68195225d50 100644
--- a/app/assets/javascripts/environments/components/environment_external_url.vue
+++ b/app/assets/javascripts/environments/components/environment_external_url.vue
@@ -29,17 +29,17 @@
<template>
<a
v-tooltip
+ :title="title"
+ :aria-label="title"
+ :href="externalUrl"
class="btn external-url"
data-container="body"
target="_blank"
rel="noopener noreferrer nofollow"
- :title="title"
- :aria-label="title"
- :href="externalUrl"
>
<icon
- name="external-link"
:size="12"
+ name="external-link"
/>
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 23aaab2c441..866e91057ec 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -427,11 +427,11 @@
</script>
<template>
<div
- class="gl-responsive-table-row"
:class="{
'js-child-row environment-child-row': model.isChildren,
'folder-row': model.isFolder,
}"
+ class="gl-responsive-table-row"
role="row">
<div
class="table-section section-10"
@@ -446,19 +446,19 @@
</div>
<a
v-if="!model.isFolder"
- class="environment-name flex-truncate-parent table-mobile-content"
- :href="environmentPath">
+ :href="environmentPath"
+ class="environment-name flex-truncate-parent table-mobile-content">
<span
- class="flex-truncate-child"
v-tooltip
:title="model.name"
+ class="flex-truncate-child"
>{{ model.name }}</span>
</a>
<span
v-else
class="folder-name"
- @click="onClickFolder"
- role="button">
+ role="button"
+ @click="onClickFolder">
<span class="folder-icon">
<i
@@ -503,11 +503,11 @@
<span v-if="!model.isFolder && deploymentHasUser">
by
<user-avatar-link
- class="js-deploy-user-container"
:link-href="deploymentUser.web_url"
:img-src="deploymentUser.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="deploymentUser.username"
+ class="js-deploy-user-container"
/>
</span>
</div>
@@ -518,8 +518,8 @@
>
<a
v-if="shouldRenderBuildName"
- class="build-link flex-truncate-parent"
:href="buildPath"
+ class="build-link flex-truncate-parent"
>
<span class="flex-truncate-child">{{ buildName }}</span>
</a>
diff --git a/app/assets/javascripts/environments/components/environment_monitoring.vue b/app/assets/javascripts/environments/components/environment_monitoring.vue
index 8df1b6317e3..947e8c901e9 100644
--- a/app/assets/javascripts/environments/components/environment_monitoring.vue
+++ b/app/assets/javascripts/environments/components/environment_monitoring.vue
@@ -28,16 +28,16 @@
<template>
<a
v-tooltip
- class="btn monitoring-url d-none d-sm-none d-md-block"
- data-container="body"
- rel="noopener noreferrer nofollow"
:href="monitoringUrl"
:title="title"
:aria-label="title"
+ class="btn monitoring-url d-none d-sm-none d-md-block"
+ data-container="body"
+ rel="noopener noreferrer nofollow"
>
<icon
- name="chart"
:size="12"
+ name="chart"
/>
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
index 7515d711c50..310835c5ea9 100644
--- a/app/assets/javascripts/environments/components/environment_rollback.vue
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -39,10 +39,10 @@
</script>
<template>
<button
+ :disabled="isLoading"
type="button"
class="btn d-none d-sm-none d-md-block"
@click="onClick"
- :disabled="isLoading"
>
<span v-if="isLastDeployment">
diff --git a/app/assets/javascripts/environments/components/environment_stop.vue b/app/assets/javascripts/environments/components/environment_stop.vue
index 7055f208451..faaaf899a0d 100644
--- a/app/assets/javascripts/environments/components/environment_stop.vue
+++ b/app/assets/javascripts/environments/components/environment_stop.vue
@@ -54,13 +54,13 @@
<template>
<button
v-tooltip
+ :disabled="isLoading"
+ :title="title"
+ :aria-label="title"
type="button"
class="btn stop-env-link d-none d-sm-none d-md-block"
data-container="body"
@click="onClick"
- :disabled="isLoading"
- :title="title"
- :aria-label="title"
>
<i
class="fa fa-stop stop-env-icon"
diff --git a/app/assets/javascripts/environments/components/environment_terminal_button.vue b/app/assets/javascripts/environments/components/environment_terminal_button.vue
index 0dbbbb75e07..f8e3165f8cd 100644
--- a/app/assets/javascripts/environments/components/environment_terminal_button.vue
+++ b/app/assets/javascripts/environments/components/environment_terminal_button.vue
@@ -30,15 +30,15 @@
<template>
<a
v-tooltip
- class="btn terminal-button d-none d-sm-none d-md-block"
- data-container="body"
:title="title"
:aria-label="title"
:href="terminalPath"
+ class="btn terminal-button d-none d-sm-none d-md-block"
+ data-container="body"
>
<icon
- name="terminal"
:size="12"
+ name="terminal"
/>
</a>
</template>
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index 3da762446c9..b18f02343d6 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -93,8 +93,8 @@
<div class="top-area">
<tabs
:tabs="tabs"
- @onChangeTab="onChangeTab"
scope="environments"
+ @onChangeTab="onChangeTab"
/>
<div
@@ -119,8 +119,8 @@
@onChangePage="onChangePage"
>
<empty-state
- slot="emptyState"
v-if="!isLoading && state.environments.length === 0"
+ slot="emptyState"
:new-path="newEnvironmentPath"
:help-path="helpPagePath"
:can-create-environment="canCreateEnvironment"
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 5ef5e347387..5f72a39c5cb 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -39,8 +39,8 @@
<template>
<div :class="cssContainerClass">
<div
- class="top-area"
v-if="!isLoading"
+ class="top-area"
>
<h4 class="js-folder-name environments-folder-name">
@@ -49,8 +49,8 @@
<tabs
:tabs="tabs"
- @onChangeTab="onChangeTab"
scope="environments"
+ @onChangeTab="onChangeTab"
/>
</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
index 26618af9515..a8eb8d94be3 100644
--- a/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
+++ b/app/assets/javascripts/filtered_search/components/recent_searches_dropdown_content.vue
@@ -72,9 +72,9 @@ export default {
@click="onItemActivated(item.text)">
<span>
<span
- class="filtered-search-history-dropdown-token"
v-for="(token, index) in item.tokens"
:key="`dropdown-token-${index}`"
+ class="filtered-search-history-dropdown-token"
>
<span class="name">{{ token.prefix }}</span>
<span class="value">{{ token.suffix }}</span>
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index 746a06b7c4f..7fbba7e27cb 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -602,7 +602,11 @@ GitLabDropdown = (function() {
var selector;
selector = '.dropdown-content';
if (this.dropdown.find(".dropdown-toggle-page").length) {
- selector = ".dropdown-page-one .dropdown-content";
+ if (this.options.containerSelector) {
+ selector = this.options.containerSelector;
+ } else {
+ selector = '.dropdown-page-one .dropdown-content';
+ }
}
return $(selector, this.dropdown).empty();
diff --git a/app/assets/javascripts/group_label_subscription.js b/app/assets/javascripts/group_label_subscription.js
index 5648cb9a888..d33e3a37580 100644
--- a/app/assets/javascripts/group_label_subscription.js
+++ b/app/assets/javascripts/group_label_subscription.js
@@ -1,7 +1,12 @@
import $ from 'jquery';
+import { __ } from '~/locale';
import axios from './lib/utils/axios_utils';
import flash from './flash';
-import { __ } from './locale';
+
+const tooltipTitles = {
+ group: __('Unsubscribe at group level'),
+ project: __('Unsubscribe at project level'),
+};
export default class GroupLabelSubscription {
constructor(container) {
@@ -35,6 +40,7 @@ export default class GroupLabelSubscription {
this.$unsubscribeButtons.attr('data-url', url);
axios.post(url)
+ .then(() => GroupLabelSubscription.setNewTooltip($btn))
.then(() => this.toggleSubscriptionButtons())
.catch(() => flash(__('There was an error when subscribing to this label.')));
}
@@ -44,4 +50,14 @@ export default class GroupLabelSubscription {
this.$subscribeButtons.toggleClass('hidden');
this.$unsubscribeButtons.toggleClass('hidden');
}
+
+ static setNewTooltip($button) {
+ if (!$button.hasClass('js-subscribe-button')) return;
+
+ const type = $button.hasClass('js-group-level') ? 'group' : 'project';
+ const newTitle = tooltipTitles[type];
+
+ $('.js-unsubscribe-button', $button.closest('.label-actions-list'))
+ .tooltip('hide').attr('title', newTitle).tooltip('_fixTitle');
+ }
}
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index 22eb7bd44c5..1def38bb336 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -216,10 +216,10 @@ export default {
<template>
<div>
<loading-icon
- class="loading-animation prepend-top-20"
- size="2"
v-if="isLoading"
:label="s__('GroupsTree|Loading groups')"
+ class="loading-animation prepend-top-20"
+ size="2"
/>
<groups-component
v-if="!isLoading"
@@ -230,10 +230,10 @@ export default {
/>
<deprecated-modal
v-show="showModal"
- kind="warning"
:primary-button-label="__('Leave')"
:title="__('Are you sure?')"
:text="groupLeaveConfirmationMessage"
+ kind="warning"
@cancel="hideLeaveGroupModal"
@submit="leaveGroup"
/>
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 7f64a9bd741..efbf2e3a295 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -71,14 +71,14 @@ export default {
<template>
<li
- @click.stop="onClickRowGroup"
:id="groupDomId"
:class="rowClass"
class="group-row"
+ @click.stop="onClickRowGroup"
>
<div
- class="group-row-contents"
- :class="{ 'project-row-contents': !isGroup }">
+ :class="{ 'project-row-contents': !isGroup }"
+ class="group-row-contents">
<item-actions
v-if="isGroup"
:group="group"
@@ -99,8 +99,8 @@ export default {
/>
</div>
<div
- class="avatar-container prepend-top-8 prepend-left-5 s24 d-none d-sm-block"
:class="{ 'content-loading': group.isChildrenLoading }"
+ class="avatar-container prepend-top-8 prepend-left-5 s24 d-none d-sm-block"
>
<a
:href="group.relativePath"
@@ -108,14 +108,14 @@ export default {
>
<img
v-if="hasAvatar"
- class="avatar s24"
:src="group.avatarUrl"
+ class="avatar s24"
/>
<identicon
v-else
- size-class="s24"
:entity-id="group.id"
:entity-name="group.name"
+ size-class="s24"
/>
</a>
</div>
diff --git a/app/assets/javascripts/groups/components/item_actions.vue b/app/assets/javascripts/groups/components/item_actions.vue
index 87065b3d6e3..24eec4901ec 100644
--- a/app/assets/javascripts/groups/components/item_actions.vue
+++ b/app/assets/javascripts/groups/components/item_actions.vue
@@ -54,13 +54,13 @@ export default {
<a
v-tooltip
v-if="group.canLeave"
- @click.prevent="onLeaveGroup"
:href="group.leavePath"
:title="leaveBtnTitle"
:aria-label="leaveBtnTitle"
data-container="body"
data-placement="bottom"
- class="leave-group btn no-expand">
+ class="leave-group btn no-expand"
+ @click.prevent="onLeaveGroup">
<icon name="leave"/>
</a>
</div>
diff --git a/app/assets/javascripts/groups/components/item_stats.vue b/app/assets/javascripts/groups/components/item_stats.vue
index 168b4e4af2c..87ab5480c15 100644
--- a/app/assets/javascripts/groups/components/item_stats.vue
+++ b/app/assets/javascripts/groups/components/item_stats.vue
@@ -45,44 +45,44 @@
<div class="stats">
<item-stats-value
v-if="isGroup"
- css-class="number-subgroups"
- icon-name="folder"
:title="__('Subgroups')"
:value="item.subgroupCount"
+ css-class="number-subgroups"
+ icon-name="folder"
/>
<item-stats-value
v-if="isGroup"
- css-class="number-projects"
- icon-name="bookmark"
:title="__('Projects')"
:value="item.projectCount"
+ css-class="number-projects"
+ icon-name="bookmark"
/>
<item-stats-value
v-if="isGroup"
- css-class="number-users"
- icon-name="users"
:title="__('Members')"
:value="item.memberCount"
+ css-class="number-users"
+ icon-name="users"
/>
<item-stats-value
v-if="isProject"
+ :value="item.starCount"
css-class="project-stars"
icon-name="star"
- :value="item.starCount"
/>
<item-stats-value
- css-class="item-visibility"
- tooltip-placement="left"
:icon-name="visibilityIcon"
:title="visibilityTooltip"
+ css-class="item-visibility"
+ tooltip-placement="left"
/>
<div
- class="last-updated"
v-if="isProject"
+ class="last-updated"
>
<time-ago-tooltip
- tooltip-placement="bottom"
:time="item.updatedAt"
+ tooltip-placement="bottom"
/>
</div>
</div>
diff --git a/app/assets/javascripts/groups/components/item_stats_value.vue b/app/assets/javascripts/groups/components/item_stats_value.vue
index 4d86ac8023c..ef9f2bca76c 100644
--- a/app/assets/javascripts/groups/components/item_stats_value.vue
+++ b/app/assets/javascripts/groups/components/item_stats_value.vue
@@ -52,10 +52,10 @@
<template>
<span
v-tooltip
- data-container="body"
:data-placement="tooltipPlacement"
:class="cssClass"
:title="title"
+ data-container="body"
>
<icon :name="iconName" />
<span
diff --git a/app/assets/javascripts/ide/components/activity_bar.vue b/app/assets/javascripts/ide/components/activity_bar.vue
index 6efcad6adea..62697e0ecc3 100644
--- a/app/assets/javascripts/ide/components/activity_bar.vue
+++ b/app/assets/javascripts/ide/components/activity_bar.vue
@@ -39,12 +39,12 @@ export default {
<li v-once>
<a
v-tooltip
- data-container="body"
- data-placement="right"
:href="goBackUrl"
- class="ide-sidebar-link"
:title="s__('IDE|Go back')"
:aria-label="s__('IDE|Go back')"
+ data-container="body"
+ data-placement="right"
+ class="ide-sidebar-link"
>
<icon
:size="16"
@@ -55,16 +55,16 @@ export default {
<li>
<button
v-tooltip
- data-container="body"
- data-placement="right"
- type="button"
- class="ide-sidebar-link js-ide-edit-mode"
:class="{
active: currentActivityView === $options.activityBarViews.edit
}"
- @click.prevent="changedActivityView($event, $options.activityBarViews.edit)"
:title="s__('IDE|Edit')"
:aria-label="s__('IDE|Edit')"
+ data-container="body"
+ data-placement="right"
+ type="button"
+ class="ide-sidebar-link js-ide-edit-mode"
+ @click.prevent="changedActivityView($event, $options.activityBarViews.edit)"
>
<icon
name="code"
@@ -74,16 +74,16 @@ export default {
<li>
<button
v-tooltip
- data-container="body"
- data-placement="right"
- type="button"
- class="ide-sidebar-link js-ide-review-mode"
:class="{
active: currentActivityView === $options.activityBarViews.review
}"
- @click.prevent="changedActivityView($event, $options.activityBarViews.review)"
:title="s__('IDE|Review')"
:aria-label="s__('IDE|Review')"
+ data-container="body"
+ data-placement="right"
+ type="button"
+ class="ide-sidebar-link js-ide-review-mode"
+ @click.prevent="changedActivityView($event, $options.activityBarViews.review)"
>
<icon
name="file-modified"
@@ -93,16 +93,16 @@ export default {
<li v-show="hasChanges">
<button
v-tooltip
- data-container="body"
- data-placement="right"
- type="button"
- class="ide-sidebar-link js-ide-commit-mode"
:class="{
active: currentActivityView === $options.activityBarViews.commit
}"
- @click.prevent="changedActivityView($event, $options.activityBarViews.commit)"
:title="s__('IDE|Commit')"
:aria-label="s__('IDE|Commit')"
+ data-container="body"
+ data-placement="right"
+ type="button"
+ class="ide-sidebar-link js-ide-commit-mode"
+ @click.prevent="changedActivityView($event, $options.activityBarViews.commit)"
>
<icon
name="commit"
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
index e2b42ab2642..955d9280728 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
@@ -91,7 +91,6 @@ export default {
<template>
<div
- class="multi-file-commit-form"
:class="{
'is-compact': isCompact,
'is-full': !isCompact
@@ -99,6 +98,7 @@ export default {
:style="{
height: componentHeight ? `${componentHeight}px` : null,
}"
+ class="multi-file-commit-form"
>
<transition
name="commit-form-slide-up"
@@ -108,12 +108,12 @@ export default {
>
<div
v-if="isCompact"
- class="commit-form-compact"
ref="compactEl"
+ class="commit-form-compact"
>
<button
- type="button"
:disabled="!hasChanges"
+ type="button"
class="btn btn-primary btn-sm btn-block"
@click="toggleIsSmall"
>
@@ -126,8 +126,8 @@ export default {
</div>
<form
v-if="!isCompact"
- @submit.prevent.stop="commitChanges"
ref="formEl"
+ @submit.prevent.stop="commitChanges"
>
<transition name="fade">
<success-message
@@ -143,8 +143,8 @@ export default {
<loading-button
:loading="submitCommitLoading"
:disabled="commitButtonDisabled"
- container-class="btn btn-success btn-sm float-left"
:label="__('Commit')"
+ container-class="btn btn-success btn-sm float-left"
@click="commitChanges"
/>
<button
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list.vue b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
index 1325fc993b2..3d59410cbc2 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list.vue
@@ -43,6 +43,15 @@ export default {
required: false,
default: false,
},
+ activeFileKey: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ keyPrefix: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -113,8 +122,9 @@ export default {
<list-item
:file="file"
:action-component="itemActionComponent"
- :key-prefix="title"
+ :key-prefix="keyPrefix"
:staged-list="stagedList"
+ :active-file-key="activeFileKey"
/>
</li>
</ul>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
index 03f3e4de83c..2ecf9af4bf0 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/list_item.vue
@@ -30,6 +30,11 @@ export default {
required: false,
default: false,
},
+ activeFileKey: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
computed: {
iconName() {
@@ -39,6 +44,12 @@ export default {
iconClass() {
return `multi-file-${this.file.tempFile ? 'addition' : 'modified'} append-right-8`;
},
+ fullKey() {
+ return `${this.keyPrefix}-${this.file.key}`;
+ },
+ isActive() {
+ return this.activeFileKey === this.fullKey;
+ },
},
methods: {
...mapActions([
@@ -51,7 +62,7 @@ export default {
openFileInEditor() {
return this.openPendingTab({
file: this.file,
- keyPrefix: this.keyPrefix.toLowerCase(),
+ keyPrefix: this.keyPrefix,
}).then(changeViewer => {
if (changeViewer) {
this.updateViewer(viewerTypes.diff);
@@ -70,7 +81,12 @@ export default {
</script>
<template>
- <div class="multi-file-commit-list-item">
+ <div
+ :class="{
+ 'is-active': isActive
+ }"
+ class="multi-file-commit-list-item"
+ >
<button
type="button"
class="multi-file-commit-list-path"
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
index 0ac0af2feaa..40496c80a46 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/message_field.vue
@@ -66,10 +66,10 @@ export default {
<template>
<fieldset class="common-note-form ide-commit-message-field">
<div
- class="md-area"
:class="{
'is-focused': isFocused
}"
+ class="md-area"
>
<div
v-once
@@ -92,10 +92,10 @@ export default {
<div class="ide-commit-message-textarea-container">
<div class="ide-commit-message-highlights-container">
<div
- class="note-textarea highlights monospace"
:style="{
transform: `translate3d(0, ${-scrollTop}px, 0)`
}"
+ class="note-textarea highlights monospace"
>
<div
v-for="(line, index) in allLines"
@@ -113,15 +113,15 @@ export default {
</div>
</div>
<textarea
- class="note-textarea ide-commit-message-textarea"
- name="commit-message"
+ ref="textarea"
:placeholder="__('Write a commit message...')"
:value="text"
+ class="note-textarea ide-commit-message-textarea"
+ name="commit-message"
@scroll="handleScroll"
@input="onInput"
@focus="updateIsFocused(true)"
@blur="updateIsFocused(false)"
- ref="textarea"
>
</textarea>
</div>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
index 00f2312ae51..35ab3fd11df 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
@@ -58,12 +58,12 @@ export default {
}"
>
<input
- type="radio"
- name="commit-action"
:value="value"
- @change="updateCommitAction($event.target.value)"
:checked="commitAction === value"
:disabled="disabled"
+ type="radio"
+ name="commit-action"
+ @change="updateCommitAction($event.target.value)"
/>
<span class="prepend-left-10">
<span
@@ -80,9 +80,9 @@ export default {
class="ide-commit-new-branch"
>
<input
+ :placeholder="newBranchName"
type="text"
class="form-control monospace"
- :placeholder="newBranchName"
@input="updateBranchName($event.target.value)"
/>
</div>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
index 52dce8412ab..a786ec80ac2 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/stage_button.vue
@@ -29,30 +29,30 @@ export default {
>
<button
v-tooltip
- type="button"
- class="btn btn-blank append-right-5"
:aria-label="__('Stage changes')"
:title="__('Stage changes')"
+ type="button"
+ class="btn btn-blank append-right-5"
data-container="body"
@click.stop="stageChange(path)"
>
<icon
- name="mobile-issue-close"
:size="12"
+ name="mobile-issue-close"
/>
</button>
<button
v-tooltip
- type="button"
- class="btn btn-blank"
:aria-label="__('Discard changes')"
:title="__('Discard changes')"
+ type="button"
+ class="btn btn-blank"
data-container="body"
@click.stop="discardFileChanges(path)"
>
<icon
- name="remove"
:size="12"
+ name="remove"
/>
</button>
</div>
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
index 123d60da47e..34b366f63ac 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/unstage_button.vue
@@ -29,16 +29,16 @@ export default {
>
<button
v-tooltip
- type="button"
- class="btn btn-blank"
:aria-label="__('Unstage changes')"
:title="__('Unstage changes')"
+ type="button"
+ class="btn btn-blank"
data-container="body"
@click="unstageChange(path)"
>
<icon
- name="history"
:size="12"
+ name="history"
/>
</button>
</div>
diff --git a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
index b9af4d27145..95598c9aca6 100644
--- a/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
+++ b/app/assets/javascripts/ide/components/editor_mode_dropdown.vue
@@ -44,11 +44,11 @@ export default {
<ul>
<li>
<a
- href="#"
- @click.prevent="changeMode($options.viewerTypes.mr)"
:class="{
'is-active': viewer === $options.viewerTypes.mr,
}"
+ href="#"
+ @click.prevent="changeMode($options.viewerTypes.mr)"
>
<strong class="dropdown-menu-inner-title">
{{ mergeReviewLine }}
@@ -60,11 +60,11 @@ export default {
</li>
<li>
<a
- href="#"
- @click.prevent="changeMode($options.viewerTypes.diff)"
:class="{
'is-active': viewer === $options.viewerTypes.diff,
}"
+ href="#"
+ @click.prevent="changeMode($options.viewerTypes.diff)"
>
<strong class="dropdown-menu-inner-title">{{ __('Reviewing') }}</strong>
<span class="dropdown-menu-inner-content">
diff --git a/app/assets/javascripts/ide/components/external_link.vue b/app/assets/javascripts/ide/components/external_link.vue
index cf3316a8179..e24fe5bbccb 100644
--- a/app/assets/javascripts/ide/components/external_link.vue
+++ b/app/assets/javascripts/ide/components/external_link.vue
@@ -26,15 +26,15 @@ export default {
>
<a
:href="file.permalink"
- target="_blank"
:title="s__('IDE|Open in file view')"
+ target="_blank"
rel="noopener noreferrer"
>
<span class="vertical-align-middle">Open in file view</span>
<icon
+ :size="16"
name="external-link"
css-classes="vertical-align-middle space-right"
- :size="16"
/>
</a>
</div>
diff --git a/app/assets/javascripts/ide/components/file_finder/index.vue b/app/assets/javascripts/ide/components/file_finder/index.vue
index cabb3f59b17..0ba33053717 100644
--- a/app/assets/javascripts/ide/components/file_finder/index.vue
+++ b/app/assets/javascripts/ide/components/file_finder/index.vue
@@ -173,38 +173,38 @@ export default {
>
<div class="dropdown-input">
<input
+ ref="searchInput"
+ :placeholder="__('Search files')"
+ v-model="searchText"
type="search"
class="dropdown-input-field"
- :placeholder="__('Search files')"
autocomplete="off"
- v-model="searchText"
- ref="searchInput"
@keydown="onKeydown($event)"
@keyup="onKeyup($event)"
/>
<i
- aria-hidden="true"
- class="fa fa-search dropdown-input-search"
:class="{
hidden: showClearInputButton
}"
+ aria-hidden="true"
+ class="fa fa-search dropdown-input-search"
></i>
<i
- role="button"
:aria-label="__('Clear search input')"
- class="fa fa-times dropdown-input-clear"
:class="{
show: showClearInputButton
}"
+ role="button"
+ class="fa fa-times dropdown-input-clear"
@click="clearSearchInput"
></i>
</div>
<div>
<virtual-list
+ ref="virtualScrollList"
:size="listHeight"
:remain="listShowCount"
wtag="ul"
- ref="virtualScrollList"
>
<template v-if="filteredBlobsLength">
<li
@@ -212,11 +212,11 @@ export default {
:key="file.key"
>
<item
- class="disable-hover"
:file="file"
:search-text="searchText"
:focused="index === focusedIndex"
:index="index"
+ class="disable-hover"
@click="openFile"
@mouseover="onMouseOver"
@mousemove="onMouseMove"
diff --git a/app/assets/javascripts/ide/components/file_finder/item.vue b/app/assets/javascripts/ide/components/file_finder/item.vue
index d4427420207..a4cf3edb981 100644
--- a/app/assets/javascripts/ide/components/file_finder/item.vue
+++ b/app/assets/javascripts/ide/components/file_finder/item.vue
@@ -59,11 +59,11 @@ export default {
<template>
<button
- type="button"
- class="diff-changed-file"
:class="{
'is-focused': focused,
}"
+ type="button"
+ class="diff-changed-file"
@click.prevent="clickRow"
@mouseover="mouseOverRow"
@mousemove="mouseMove"
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index f5f832521c5..f5f7f967a92 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -93,8 +93,8 @@ export default {
:merge-request-id="currentMergeRequestId"
/>
<repo-editor
- class="multi-file-edit-pane-content"
:file="activeFile"
+ class="multi-file-edit-pane-content"
/>
</template>
<template
diff --git a/app/assets/javascripts/ide/components/ide_review.vue b/app/assets/javascripts/ide/components/ide_review.vue
index 99fa2465a84..f9978762c45 100644
--- a/app/assets/javascripts/ide/components/ide_review.vue
+++ b/app/assets/javascripts/ide/components/ide_review.vue
@@ -36,8 +36,8 @@ export default {
<template>
<ide-tree-list
:viewer-type="viewer"
- header-class="ide-review-header"
:disable-action-dropdown="true"
+ header-class="ide-review-header"
>
<template
slot="header"
diff --git a/app/assets/javascripts/ide/components/ide_side_bar.vue b/app/assets/javascripts/ide/components/ide_side_bar.vue
index 1dc2170edde..21906674c4b 100644
--- a/app/assets/javascripts/ide/components/ide_side_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_side_bar.vue
@@ -115,17 +115,17 @@ export default {
<div class="multi-file-commit-panel-inner">
<template v-if="loading">
<div
- class="multi-file-loading-container"
v-for="n in 3"
:key="n"
+ class="multi-file-loading-container"
>
<skeleton-loading-container />
</div>
</template>
<template v-else>
<div
- class="context-header ide-context-header dropdown"
ref="mergeRequestDropdown"
+ class="context-header ide-context-header dropdown"
>
<button
type="button"
@@ -136,18 +136,18 @@ export default {
class="avatar-container s40 project-avatar"
>
<project-avatar-image
- class="avatar-container project-avatar"
:link-href="currentProject.path"
:img-src="currentProject.avatar_url"
:img-alt="currentProject.name"
:img-size="40"
+ class="avatar-container project-avatar"
/>
</div>
<identicon
v-else
- size-class="s40"
:entity-id="currentProject.id"
:entity-name="currentProject.name"
+ size-class="s40"
/>
<div class="ide-sidebar-project-title">
<div class="sidebar-context-title">
@@ -155,11 +155,11 @@ export default {
</div>
<div class="d-flex">
<div
+ v-tooltip
v-if="currentBranchId"
- class="sidebar-context-title ide-sidebar-branch-title"
ref="branchId"
- v-tooltip
:title="branchTooltipTitle"
+ class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="branch"
@@ -168,10 +168,10 @@ export default {
</div>
<div
v-if="currentMergeRequestId"
- class="sidebar-context-title ide-sidebar-branch-title"
:class="{
'prepend-left-8': currentBranchId
}"
+ class="sidebar-context-title ide-sidebar-branch-title"
>
<icon
name="git-merge"
diff --git a/app/assets/javascripts/ide/components/ide_status_bar.vue b/app/assets/javascripts/ide/components/ide_status_bar.vue
index e40f137d998..0582ad32e92 100644
--- a/app/assets/javascripts/ide/components/ide_status_bar.vue
+++ b/app/assets/javascripts/ide/components/ide_status_bar.vue
@@ -75,22 +75,22 @@ export default {
<template>
<footer class="ide-status-bar">
<div
- class="ide-status-branch"
v-if="lastCommit && lastCommitFormatedAge"
+ class="ide-status-branch"
>
<span
- class="ide-status-pipeline"
v-if="latestPipeline && latestPipeline.details"
+ class="ide-status-pipeline"
>
<ci-icon
- :status="latestPipeline.details.status"
v-tooltip
+ :status="latestPipeline.details.status"
:title="latestPipeline.details.status.text"
/>
Pipeline
<a
- class="monospace"
- :href="latestPipeline.details.status.details_path">#{{ latestPipeline.id }}</a>
+ :href="latestPipeline.details.status.details_path"
+ class="monospace">#{{ latestPipeline.id }}</a>
{{ latestPipeline.details.status.text }}
for
</span>
@@ -100,18 +100,18 @@ export default {
/>
<a
v-tooltip
- class="commit-sha"
:title="lastCommit.message"
:href="getCommitPath(lastCommit.short_id)"
+ class="commit-sha"
>{{ lastCommit.short_id }}</a>
by
{{ lastCommit.author_name }}
<time
v-tooltip
- data-placement="top"
- data-container="body"
:datetime="lastCommit.committed_date"
:title="tooltipTitle(lastCommit.committed_date)"
+ data-placement="top"
+ data-container="body"
>
{{ lastCommitFormatedAge }}
</time>
@@ -129,8 +129,8 @@ export default {
{{ file.eol }}
</div>
<div
- class="ide-status-file"
- v-if="file && !file.binary">
+ v-if="file && !file.binary"
+ class="ide-status-file">
{{ file.editorRow }}:{{ file.editorColumn }}
</div>
<div
diff --git a/app/assets/javascripts/ide/components/ide_tree_list.vue b/app/assets/javascripts/ide/components/ide_tree_list.vue
index e64a09fcc90..0df99798d21 100644
--- a/app/assets/javascripts/ide/components/ide_tree_list.vue
+++ b/app/assets/javascripts/ide/components/ide_tree_list.vue
@@ -50,17 +50,17 @@ export default {
>
<template v-if="showLoading">
<div
- class="multi-file-loading-container"
v-for="n in 3"
:key="n"
+ class="multi-file-loading-container"
>
<skeleton-loading-container />
</div>
</template>
<template v-else>
<header
- class="ide-tree-header"
:class="headerClass"
+ class="ide-tree-header"
>
<slot name="header"></slot>
</header>
diff --git a/app/assets/javascripts/ide/components/jobs/detail.vue b/app/assets/javascripts/ide/components/jobs/detail.vue
index 4d234a36fe5..f39ce545656 100644
--- a/app/assets/javascripts/ide/components/jobs/detail.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail.vue
@@ -93,10 +93,10 @@ export default {
<a
v-tooltip
:title="__('Show complete raw log')"
+ :href="detailJob.rawPath"
data-placement="top"
data-container="body"
class="controllers-buttons"
- :href="detailJob.rawPath"
target="_blank"
>
<i
@@ -105,23 +105,24 @@ export default {
></i>
</a>
<scroll-button
- direction="up"
:disabled="isScrolledToTop"
+ direction="up"
@click="scrollUp"
/>
<scroll-button
- direction="down"
:disabled="isScrolledToBottom"
+ direction="down"
@click="scrollDown"
/>
</div>
</div>
<pre
- class="build-trace mb-0 h-100"
ref="buildTrace"
+ class="build-trace mb-0 h-100"
@scroll="scrollBuildLog"
>
<code
+ v-show="!detailJob.isLoading"
class="bash"
v-html="jobOutput"
>
diff --git a/app/assets/javascripts/ide/components/jobs/detail/description.vue b/app/assets/javascripts/ide/components/jobs/detail/description.vue
index def6bac3157..7e24974f7e5 100644
--- a/app/assets/javascripts/ide/components/jobs/detail/description.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail/description.vue
@@ -24,10 +24,10 @@ export default {
<template>
<div class="d-flex align-items-center">
<ci-icon
- class="d-flex"
:status="job.status"
:borderless="true"
:size="24"
+ class="d-flex"
/>
<span class="prepend-left-8">
{{ job.name }}
@@ -38,8 +38,8 @@ export default {
>
{{ jobId }}
<icon
- name="external-link"
:size="12"
+ name="external-link"
/>
</a>
</span>
diff --git a/app/assets/javascripts/ide/components/jobs/detail/scroll_button.vue b/app/assets/javascripts/ide/components/jobs/detail/scroll_button.vue
index 4e19e6e9c84..103a407987f 100644
--- a/app/assets/javascripts/ide/components/jobs/detail/scroll_button.vue
+++ b/app/assets/javascripts/ide/components/jobs/detail/scroll_button.vue
@@ -47,15 +47,15 @@ export default {
<template>
<div
v-tooltip
+ :title="tooltipTitle"
class="controllers-buttons"
data-container="body"
data-placement="top"
- :title="tooltipTitle"
>
<button
+ :disabled="disabled"
class="btn-scroll btn-transparent btn-blank"
type="button"
- :disabled="disabled"
@click="clickedScroll"
>
<icon
diff --git a/app/assets/javascripts/ide/components/jobs/item.vue b/app/assets/javascripts/ide/components/jobs/item.vue
index c8e621504f0..7f4695a0451 100644
--- a/app/assets/javascripts/ide/components/jobs/item.vue
+++ b/app/assets/javascripts/ide/components/jobs/item.vue
@@ -27,8 +27,8 @@ export default {
<template>
<div class="ide-job-item">
<job-description
- class="append-right-default"
:job="job"
+ class="append-right-default"
/>
<div class="ml-auto align-self-center">
<button
diff --git a/app/assets/javascripts/ide/components/jobs/stage.vue b/app/assets/javascripts/ide/components/jobs/stage.vue
index b1428f885fb..15e881b7bc8 100644
--- a/app/assets/javascripts/ide/components/jobs/stage.vue
+++ b/app/assets/javascripts/ide/components/jobs/stage.vue
@@ -60,10 +60,10 @@ export default {
class="ide-stage card prepend-top-default"
>
<div
- class="card-header"
:class="{
'border-bottom-0': stage.isCollapsed
}"
+ class="card-header"
@click="toggleCollapsed"
>
<ci-icon
@@ -72,10 +72,10 @@ export default {
/>
<strong
v-tooltip="showTooltip"
+ ref="stageTitle"
:title="showTooltip ? stage.name : null"
data-container="body"
class="prepend-left-8 ide-stage-title"
- ref="stageTitle"
>
{{ stage.name }}
</strong>
@@ -93,8 +93,8 @@ export default {
/>
</div>
<div
- class="card-body"
v-show="!stage.isCollapsed"
+ class="card-body"
>
<loading-icon
v-if="showLoadingIcon"
diff --git a/app/assets/javascripts/ide/components/merge_requests/dropdown.vue b/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
index 8cc8345db2e..4b9824bf04b 100644
--- a/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/dropdown.vue
@@ -42,8 +42,8 @@ export default {
</span>
</template>
<list
- type="created"
:empty-text="__('You have not created any merge requests')"
+ type="created"
/>
</tab>
<tab>
@@ -54,8 +54,8 @@ export default {
</span>
</template>
<list
- type="assigned"
:empty-text="__('You do not have any assigned merge requests')"
+ type="assigned"
/>
</tab>
</tabs>
diff --git a/app/assets/javascripts/ide/components/merge_requests/item.vue b/app/assets/javascripts/ide/components/merge_requests/item.vue
index b50fc8a3dbb..4e18376bd48 100644
--- a/app/assets/javascripts/ide/components/merge_requests/item.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/item.vue
@@ -47,8 +47,8 @@ export default {
<span class="d-flex append-right-default ide-merge-request-current-icon">
<icon
v-if="isActive"
- name="mobile-issue-close"
:size="18"
+ name="mobile-issue-close"
/>
</span>
<span>
diff --git a/app/assets/javascripts/ide/components/merge_requests/list.vue b/app/assets/javascripts/ide/components/merge_requests/list.vue
index 5896e3a147d..19d3e48ee10 100644
--- a/app/assets/javascripts/ide/components/merge_requests/list.vue
+++ b/app/assets/javascripts/ide/components/merge_requests/list.vue
@@ -80,12 +80,12 @@ export default {
<div>
<div class="dropdown-input mt-3 pb-3 mb-0 border-bottom">
<input
- type="search"
- class="dropdown-input-field"
+ ref="searchInput"
:placeholder="__('Search merge requests')"
v-model="search"
+ type="search"
+ class="dropdown-input-field"
@input="searchMergeRequests"
- ref="searchInput"
/>
<i
aria-hidden="true"
@@ -94,8 +94,8 @@ export default {
</div>
<div class="dropdown-content ide-merge-requests-dropdown-content d-flex">
<loading-icon
- class="mt-3 mb-3 align-self-center ml-auto mr-auto"
v-if="isLoading"
+ class="mt-3 mb-3 align-self-center ml-auto mr-auto"
size="2"
/>
<ul
diff --git a/app/assets/javascripts/ide/components/mr_file_icon.vue b/app/assets/javascripts/ide/components/mr_file_icon.vue
index 179a589d1ac..821be319cce 100644
--- a/app/assets/javascripts/ide/components/mr_file_icon.vue
+++ b/app/assets/javascripts/ide/components/mr_file_icon.vue
@@ -14,10 +14,10 @@ export default {
<template>
<icon
- name="git-merge"
v-tooltip
:title="__('Part of merge request changes')"
- css-classes="append-right-8"
:size="12"
+ name="git-merge"
+ css-classes="append-right-8"
/>
</template>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index f0b29702497..1e398d7e1aa 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -55,10 +55,10 @@ export default {
<template>
<div class="ide-new-btn">
<div
- class="dropdown"
:class="{
show: dropdownOpen,
}"
+ class="dropdown"
>
<button
type="button"
@@ -67,19 +67,19 @@ export default {
@click.stop="openDropdown()"
>
<icon
- name="plus"
:size="12"
+ name="plus"
css-classes="float-left"
/>
<icon
- name="arrow-down"
:size="12"
+ name="arrow-down"
css-classes="float-left"
/>
</button>
<ul
- class="dropdown-menu dropdown-menu-right"
ref="dropdownMenu"
+ class="dropdown-menu dropdown-menu-right"
>
<li>
<a
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index dd2800179ff..1e9668d5154 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -71,18 +71,18 @@ export default {
>
<form
slot="body"
- @submit.prevent="createEntryInStore"
class="form-group row"
+ @submit.prevent="createEntryInStore"
>
<label class="label-light col-form-label col-sm-3">
{{ __('Name') }}
</label>
<div class="col-sm-9">
<input
+ ref="fieldName"
+ v-model="entryName"
type="text"
class="form-control"
- v-model="entryName"
- ref="fieldName"
/>
</div>
</form>
diff --git a/app/assets/javascripts/ide/components/new_dropdown/upload.vue b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
index c165af5ce52..1814924be39 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/upload.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/upload.vue
@@ -67,9 +67,9 @@
</a>
<input
id="file-upload"
+ ref="fileUpload"
type="file"
class="hidden"
- ref="fileUpload"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/panes/right.vue b/app/assets/javascripts/ide/components/panes/right.vue
index aafd6a15a78..dedc2988618 100644
--- a/app/assets/javascripts/ide/components/panes/right.vue
+++ b/app/assets/javascripts/ide/components/panes/right.vue
@@ -5,6 +5,7 @@ import Icon from '../../../vue_shared/components/icon.vue';
import { rightSidebarViews } from '../../constants';
import PipelinesList from '../pipelines/list.vue';
import JobsDetail from '../jobs/detail.vue';
+import ResizablePanel from '../resizable_panel.vue';
export default {
directives: {
@@ -14,6 +15,7 @@ export default {
Icon,
PipelinesList,
JobsDetail,
+ ResizablePanel,
},
computed: {
...mapState(['rightPane']),
@@ -40,24 +42,28 @@ export default {
<div
class="multi-file-commit-panel ide-right-sidebar"
>
- <div
- class="multi-file-commit-panel-inner"
+ <resizable-panel
v-if="rightPane"
+ :collapsible="false"
+ :initial-width="350"
+ :min-size="350"
+ class="multi-file-commit-panel-inner"
+ side="right"
>
<component :is="rightPane" />
- </div>
+ </resizable-panel>
<nav class="ide-activity-bar">
<ul class="list-unstyled">
<li>
<button
v-tooltip
- data-container="body"
- data-placement="left"
:title="__('Pipelines')"
- class="ide-sidebar-link is-right"
:class="{
active: pipelinesActive
}"
+ data-container="body"
+ data-placement="left"
+ class="ide-sidebar-link is-right"
type="button"
@click="clickTab($event, $options.rightSidebarViews.pipelines)"
>
diff --git a/app/assets/javascripts/ide/components/pipelines/list.vue b/app/assets/javascripts/ide/components/pipelines/list.vue
index 06455fac439..5757dfdc925 100644
--- a/app/assets/javascripts/ide/components/pipelines/list.vue
+++ b/app/assets/javascripts/ide/components/pipelines/list.vue
@@ -75,8 +75,8 @@ export default {
>
#{{ latestPipeline.id }}
<icon
- name="external-link"
:size="12"
+ name="external-link"
/>
</a>
</span>
@@ -94,7 +94,7 @@ export default {
<p class="append-bottom-0">
{{ __('Found errors in your .gitlab-ci.yml:') }}
</p>
- <p class="append-bottom-0">
+ <p class="append-bottom-0 break-word">
{{ latestPipeline.yamlError }}
</p>
<p
diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue
index c5092d8e04d..01df0019fd4 100644
--- a/app/assets/javascripts/ide/components/repo_commit_section.vue
+++ b/app/assets/javascripts/ide/components/repo_commit_section.vue
@@ -6,7 +6,7 @@ import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import CommitFilesList from './commit_sidebar/list.vue';
import EmptyState from './commit_sidebar/empty_state.vue';
import * as consts from '../stores/modules/commit/constants';
-import { activityBarViews } from '../constants';
+import { activityBarViews, stageKeys } from '../constants';
export default {
components: {
@@ -27,11 +27,14 @@ export default {
'unusedSeal',
]),
...mapState('commit', ['commitMessage', 'submitCommitLoading']),
- ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges']),
+ ...mapGetters(['lastOpenedFile', 'hasChanges', 'someUncommitedChanges', 'activeFile']),
...mapGetters('commit', ['commitButtonDisabled', 'discardDraftButtonDisabled']),
showStageUnstageArea() {
return !!(this.someUncommitedChanges || this.lastCommitMsg || !this.unusedSeal);
},
+ activeFileKey() {
+ return this.activeFile ? this.activeFile.key : null;
+ },
},
watch: {
hasChanges() {
@@ -44,6 +47,7 @@ export default {
if (this.lastOpenedFile) {
this.openPendingTab({
file: this.lastOpenedFile,
+ keyPrefix: this.lastOpenedFile.changed ? stageKeys.unstaged : stageKeys.staged,
})
.then(changeViewer => {
if (changeViewer) {
@@ -62,6 +66,7 @@ export default {
return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges());
},
},
+ stageKeys,
};
</script>
@@ -72,8 +77,8 @@ export default {
<deprecated-modal
id="ide-create-branch-modal"
:primary-button-label="__('Create new branch')"
- kind="success"
:title="__('Branch has changed')"
+ kind="success"
@submit="forceCreateNewBranch"
>
<template slot="body">
@@ -85,22 +90,26 @@ export default {
v-if="showStageUnstageArea"
>
<commit-files-list
- class="is-first"
- icon-name="unstaged"
:title="__('Unstaged')"
+ :key-prefix="$options.stageKeys.unstaged"
:file-list="changedFiles"
- action="stageAllChanges"
:action-btn-text="__('Stage all')"
+ :active-file-key="activeFileKey"
+ class="is-first"
+ icon-name="unstaged"
+ action="stageAllChanges"
item-action-component="stage-button"
/>
<commit-files-list
- icon-name="staged"
:title="__('Staged')"
+ :key-prefix="$options.stageKeys.staged"
:file-list="stagedFiles"
- action="unstageAllChanges"
:action-btn-text="__('Unstage all')"
- item-action-component="unstage-button"
:staged-list="true"
+ :active-file-key="activeFileKey"
+ icon-name="staged"
+ action="unstageAllChanges"
+ item-action-component="unstage-button"
/>
</template>
<empty-state
diff --git a/app/assets/javascripts/ide/components/repo_editor.vue b/app/assets/javascripts/ide/components/repo_editor.vue
index 93453989c08..24b6a4fdea1 100644
--- a/app/assets/javascripts/ide/components/repo_editor.vue
+++ b/app/assets/javascripts/ide/components/repo_editor.vue
@@ -1,16 +1,16 @@
<script>
-/* global monaco */
import { mapState, mapGetters, mapActions } from 'vuex';
import flash from '~/flash';
import ContentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
+import DiffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
import { activityBarViews, viewerTypes } from '../constants';
-import monacoLoader from '../monaco_loader';
import Editor from '../lib/editor';
import ExternalLink from './external_link.vue';
export default {
components: {
ContentViewer,
+ DiffViewer,
ExternalLink,
},
props: {
@@ -31,9 +31,18 @@ export default {
shouldHideEditor() {
return this.file && this.file.binary && !this.file.content;
},
+ showContentViewer() {
+ return (
+ (this.shouldHideEditor || this.file.viewMode === 'preview') &&
+ (this.viewer !== viewerTypes.mr || !this.file.mrChange)
+ );
+ },
+ showDiffViewer() {
+ return this.shouldHideEditor && this.file.mrChange && this.viewer === viewerTypes.mr;
+ },
editTabCSS() {
return {
- active: this.file.viewMode === 'edit',
+ active: this.file.viewMode === 'editor',
};
},
previewTabCSS() {
@@ -50,12 +59,12 @@ export default {
// Compare key to allow for files opened in review mode to be cached differently
if (oldVal.key !== this.file.key) {
- this.initMonaco();
+ this.initEditor();
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
file: this.file,
- viewMode: 'edit',
+ viewMode: 'editor',
});
}
}
@@ -64,7 +73,7 @@ export default {
if (this.currentActivityView !== activityBarViews.edit) {
this.setFileViewMode({
file: this.file,
- viewMode: 'edit',
+ viewMode: 'editor',
});
}
},
@@ -84,15 +93,10 @@ export default {
this.editor.dispose();
},
mounted() {
- if (this.editor && monaco) {
- this.initMonaco();
- } else {
- monacoLoader(['vs/editor/editor.main'], () => {
- this.editor = Editor.create(monaco);
-
- this.initMonaco();
- });
+ if (!this.editor) {
+ this.editor = Editor.create();
}
+ this.initEditor();
},
methods: {
...mapActions([
@@ -105,7 +109,7 @@ export default {
'updateViewer',
'removePendingTab',
]),
- initMonaco() {
+ initEditor() {
if (this.shouldHideEditor) return;
this.editor.clearEditor();
@@ -118,7 +122,7 @@ export default {
this.createEditorInstance();
})
.catch(err => {
- flash('Error setting up monaco. Please try again.', 'alert', document, null, false, true);
+ flash('Error setting up editor. Please try again.', 'alert', document, null, false, true);
throw err;
});
},
@@ -197,14 +201,14 @@ export default {
>
<div class="ide-mode-tabs clearfix" >
<ul
- class="nav-links float-left"
v-if="!shouldHideEditor && isEditModeActive"
+ class="nav-links float-left"
>
<li :class="editTabCSS">
<a
href="javascript:void(0);"
role="button"
- @click.prevent="setFileViewMode({ file, viewMode: 'edit' })">
+ @click.prevent="setFileViewMode({ file, viewMode: 'editor' })">
<template v-if="viewer === $options.viewerTypes.edit">
{{ __('Edit') }}
</template>
@@ -229,19 +233,27 @@ export default {
/>
</div>
<div
- v-show="!shouldHideEditor && file.viewMode === 'edit'"
+ v-show="!shouldHideEditor && file.viewMode ==='editor'"
ref="editor"
- class="multi-file-editor-holder"
:class="{
'is-readonly': isCommitModeActive,
}"
+ class="multi-file-editor-holder"
>
</div>
<content-viewer
- v-if="shouldHideEditor || file.viewMode === 'preview'"
+ v-if="showContentViewer"
:content="file.content || file.raw"
:path="file.rawPath || file.path"
:file-size="file.size"
:project-path="file.projectId"/>
+ <diff-viewer
+ v-if="showDiffViewer"
+ :diff-mode="file.mrChange.diffMode"
+ :new-path="file.mrChange.new_path"
+ :new-sha="currentMergeRequest.sha"
+ :old-path="file.mrChange.old_path"
+ :old-sha="currentMergeRequest.baseCommitSha"
+ :project-path="file.projectId"/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/components/repo_file.vue b/app/assets/javascripts/ide/components/repo_file.vue
index f56aeced806..c34547fcc60 100644
--- a/app/assets/javascripts/ide/components/repo_file.vue
+++ b/app/assets/javascripts/ide/components/repo_file.vue
@@ -120,17 +120,17 @@ export default {
<template>
<div>
<div
- class="file"
:class="fileClass"
- @click="clickFile"
+ class="file"
role="button"
+ @click="clickFile"
>
<div
class="file-name"
>
<span
- class="ide-file-name str-truncated"
:style="levelIndentation"
+ class="ide-file-name str-truncated"
>
<file-icon
:file-name="file.name"
@@ -156,10 +156,10 @@ export default {
<icon
v-tooltip
:title="folderChangesTooltip"
+ :size="12"
data-container="body"
data-placement="right"
name="file-modified"
- :size="12"
css-classes="prepend-left-5 multi-file-modified"
/>
</span>
diff --git a/app/assets/javascripts/ide/components/repo_file_status_icon.vue b/app/assets/javascripts/ide/components/repo_file_status_icon.vue
index 97589e116c5..76a3333be50 100644
--- a/app/assets/javascripts/ide/components/repo_file_status_icon.vue
+++ b/app/assets/javascripts/ide/components/repo_file_status_icon.vue
@@ -26,8 +26,8 @@ export default {
<template>
<span
- v-if="file.file_lock"
v-tooltip
+ v-if="file.file_lock"
:title="lockTooltip"
data-container="body"
>
diff --git a/app/assets/javascripts/ide/components/repo_loading_file.vue b/app/assets/javascripts/ide/components/repo_loading_file.vue
index 3e47da88050..7a5ede82253 100644
--- a/app/assets/javascripts/ide/components/repo_loading_file.vue
+++ b/app/assets/javascripts/ide/components/repo_loading_file.vue
@@ -33,8 +33,8 @@
<td class="d-none d-sm-block">
<skeleton-loading-container
- class="animation-container-right"
:small="true"
+ class="animation-container-right"
/>
</td>
</template>
diff --git a/app/assets/javascripts/ide/components/repo_tab.vue b/app/assets/javascripts/ide/components/repo_tab.vue
index fb26b973236..1ad52c1bd83 100644
--- a/app/assets/javascripts/ide/components/repo_tab.vue
+++ b/app/assets/javascripts/ide/components/repo_tab.vue
@@ -76,8 +76,8 @@ export default {
@mouseout="mouseOutTab"
>
<div
- class="multi-file-tab"
:title="tab.url"
+ class="multi-file-tab"
>
<file-icon
:file-name="tab.name"
@@ -89,16 +89,16 @@ export default {
/>
</div>
<button
+ :aria-label="closeLabel"
+ :disabled="tab.pending"
type="button"
class="multi-file-tab-close"
@click.stop.prevent="closeFile(tab)"
- :aria-label="closeLabel"
- :disabled="tab.pending"
>
<icon
v-if="!showChangedIcon"
- name="close"
:size="12"
+ name="close"
/>
<changed-file-icon
v-else
diff --git a/app/assets/javascripts/ide/components/repo_tabs.vue b/app/assets/javascripts/ide/components/repo_tabs.vue
index 99e51097e12..c12a63e26be 100644
--- a/app/assets/javascripts/ide/components/repo_tabs.vue
+++ b/app/assets/javascripts/ide/components/repo_tabs.vue
@@ -52,8 +52,8 @@ export default {
<template>
<div class="multi-file-tabs">
<ul
- class="list-unstyled append-bottom-0"
ref="tabsScroller"
+ class="list-unstyled append-bottom-0"
>
<repo-tab
v-for="tab in files"
diff --git a/app/assets/javascripts/ide/components/resizable_panel.vue b/app/assets/javascripts/ide/components/resizable_panel.vue
index 5ea2a2f6825..7277fcb7617 100644
--- a/app/assets/javascripts/ide/components/resizable_panel.vue
+++ b/app/assets/javascripts/ide/components/resizable_panel.vue
@@ -63,11 +63,11 @@ export default {
<template>
<div
- class="multi-file-commit-panel"
:class="{
'is-collapsed': collapsed && collapsible,
}"
:style="panelStyle"
+ class="multi-file-commit-panel"
@click="toggleFullbarCollapsed"
>
<slot></slot>
@@ -77,9 +77,9 @@ export default {
:start-size="initialWidth"
:min-size="minSize"
:max-size="$options.maxSize"
+ :side="side === 'right' ? 'left' : 'right'"
@resize-start="setResizingStatus(true)"
@resize-end="setResizingStatus(false)"
- :side="side === 'right' ? 'left' : 'right'"
/>
</div>
</template>
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 65886c02b92..12e0c3aeef0 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -21,7 +21,19 @@ export const viewerTypes = {
diff: 'diff',
};
+export const diffModes = {
+ replaced: 'replaced',
+ new: 'new',
+ deleted: 'deleted',
+ renamed: 'renamed',
+};
+
export const rightSidebarViews = {
pipelines: 'pipelines-list',
jobsDetail: 'jobs-detail',
};
+
+export const stageKeys = {
+ unstaged: 'unstaged',
+ staged: 'staged',
+};
diff --git a/app/assets/javascripts/ide/lib/common/model.js b/app/assets/javascripts/ide/lib/common/model.js
index e5149b1f3ad..78e6f632728 100644
--- a/app/assets/javascripts/ide/lib/common/model.js
+++ b/app/assets/javascripts/ide/lib/common/model.js
@@ -1,32 +1,32 @@
+import { editor as monacoEditor, Uri } from 'monaco-editor';
import Disposable from './disposable';
import eventHub from '../../eventhub';
export default class Model {
- constructor(monaco, file, head = null) {
- this.monaco = monaco;
+ constructor(file, head = null) {
this.disposable = new Disposable();
this.file = file;
this.head = head;
this.content = file.content !== '' ? file.content : file.raw;
this.disposable.add(
- (this.originalModel = this.monaco.editor.createModel(
+ (this.originalModel = monacoEditor.createModel(
head ? head.content : this.file.raw,
undefined,
- new this.monaco.Uri(null, null, `original/${this.path}`),
+ new Uri(false, false, `original/${this.path}`),
)),
- (this.model = this.monaco.editor.createModel(
+ (this.model = monacoEditor.createModel(
this.content,
undefined,
- new this.monaco.Uri(null, null, this.path),
+ new Uri(false, false, this.path),
)),
);
if (this.file.mrChange) {
this.disposable.add(
- (this.baseModel = this.monaco.editor.createModel(
+ (this.baseModel = monacoEditor.createModel(
this.file.baseRaw,
undefined,
- new this.monaco.Uri(null, null, `target/${this.path}`),
+ new Uri(false, false, `target/${this.path}`),
)),
);
}
diff --git a/app/assets/javascripts/ide/lib/common/model_manager.js b/app/assets/javascripts/ide/lib/common/model_manager.js
index 7f643969480..bd9b8fc3fcc 100644
--- a/app/assets/javascripts/ide/lib/common/model_manager.js
+++ b/app/assets/javascripts/ide/lib/common/model_manager.js
@@ -3,8 +3,7 @@ import Disposable from './disposable';
import Model from './model';
export default class ModelManager {
- constructor(monaco) {
- this.monaco = monaco;
+ constructor() {
this.disposable = new Disposable();
this.models = new Map();
}
@@ -22,7 +21,7 @@ export default class ModelManager {
return this.getModel(file.key);
}
- const model = new Model(this.monaco, file, head);
+ const model = new Model(file, head);
this.models.set(model.path, model);
this.disposable.add(model);
diff --git a/app/assets/javascripts/ide/lib/diff/controller.js b/app/assets/javascripts/ide/lib/diff/controller.js
index f579424cf33..046e562ba2b 100644
--- a/app/assets/javascripts/ide/lib/diff/controller.js
+++ b/app/assets/javascripts/ide/lib/diff/controller.js
@@ -1,4 +1,4 @@
-/* global monaco */
+import { Range } from 'monaco-editor';
import { throttle } from 'underscore';
import DirtyDiffWorker from './diff_worker';
import Disposable from '../common/disposable';
@@ -16,7 +16,7 @@ export const getDiffChangeType = change => {
};
export const getDecorator = change => ({
- range: new monaco.Range(change.lineNumber, 1, change.endLineNumber, 1),
+ range: new Range(change.lineNumber, 1, change.endLineNumber, 1),
options: {
isWholeLine: true,
linesDecorationsClassName: `dirty-diff dirty-diff-${getDiffChangeType(change)}`,
diff --git a/app/assets/javascripts/ide/lib/editor.js b/app/assets/javascripts/ide/lib/editor.js
index 9c3bb9cc17d..02038fcb534 100644
--- a/app/assets/javascripts/ide/lib/editor.js
+++ b/app/assets/javascripts/ide/lib/editor.js
@@ -1,4 +1,5 @@
import _ from 'underscore';
+import { editor as monacoEditor, KeyCode, KeyMod } from 'monaco-editor';
import store from '../stores';
import DecorationsController from './decorations/controller';
import DirtyDiffController from './diff/controller';
@@ -8,6 +9,11 @@ import editorOptions, { defaultEditorOptions } from './editor_options';
import gitlabTheme from './themes/gl_theme';
import keymap from './keymap.json';
+function setupMonacoTheme() {
+ monacoEditor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
+ monacoEditor.setTheme('gitlab');
+}
+
export const clearDomElement = el => {
if (!el || !el.firstChild) return;
@@ -17,24 +23,22 @@ export const clearDomElement = el => {
};
export default class Editor {
- static create(monaco) {
- if (this.editorInstance) return this.editorInstance;
-
- this.editorInstance = new Editor(monaco);
-
+ static create() {
+ if (!this.editorInstance) {
+ this.editorInstance = new Editor();
+ }
return this.editorInstance;
}
- constructor(monaco) {
- this.monaco = monaco;
+ constructor() {
this.currentModel = null;
this.instance = null;
this.dirtyDiffController = null;
this.disposable = new Disposable();
- this.modelManager = new ModelManager(this.monaco);
+ this.modelManager = new ModelManager();
this.decorationsController = new DecorationsController(this);
- this.setupMonacoTheme();
+ setupMonacoTheme();
this.debouncedUpdate = _.debounce(() => {
this.updateDimensions();
@@ -46,7 +50,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
- (this.instance = this.monaco.editor.create(domElement, {
+ (this.instance = monacoEditor.create(domElement, {
...defaultEditorOptions,
})),
(this.dirtyDiffController = new DirtyDiffController(
@@ -66,7 +70,7 @@ export default class Editor {
clearDomElement(domElement);
this.disposable.add(
- (this.instance = this.monaco.editor.createDiffEditor(domElement, {
+ (this.instance = monacoEditor.createDiffEditor(domElement, {
...defaultEditorOptions,
quickSuggestions: false,
occurrencesHighlight: false,
@@ -122,17 +126,11 @@ export default class Editor {
modified: model.getModel(),
});
- this.monaco.editor.createDiffNavigator(this.instance, {
+ monacoEditor.createDiffNavigator(this.instance, {
alwaysRevealFirst: true,
});
}
- setupMonacoTheme() {
- this.monaco.editor.defineTheme(gitlabTheme.themeName, gitlabTheme.monacoTheme);
-
- this.monaco.editor.setTheme('gitlab');
- }
-
clearEditor() {
if (this.instance) {
this.instance.setModel(null);
@@ -200,7 +198,7 @@ export default class Editor {
const getKeyCode = key => {
const monacoKeyMod = key.indexOf('KEY_') === 0;
- return monacoKeyMod ? this.monaco.KeyCode[key] : this.monaco.KeyMod[key];
+ return monacoKeyMod ? KeyCode[key] : KeyMod[key];
};
keymap.forEach(command => {
diff --git a/app/assets/javascripts/ide/monaco_loader.js b/app/assets/javascripts/ide/monaco_loader.js
deleted file mode 100644
index 142a220097b..00000000000
--- a/app/assets/javascripts/ide/monaco_loader.js
+++ /dev/null
@@ -1,16 +0,0 @@
-import monacoContext from 'monaco-editor/dev/vs/loader';
-
-monacoContext.require.config({
- paths: {
- vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
- },
-});
-
-// 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/ide/stores/modules/merge_requests/actions.js b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
index 5beb8fac71f..4e1df80b3a2 100644
--- a/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/merge_requests/actions.js
@@ -31,10 +31,15 @@ export const openMergeRequest = ({ commit, dispatch }, { projectPath, id }) => {
commit(rootTypes.CLEAR_PROJECTS, null, { root: true });
commit(rootTypes.SET_CURRENT_MERGE_REQUEST, `${id}`, { root: true });
commit(rootTypes.RESET_OPEN_FILES, null, { root: true });
- dispatch('pipelines/stopPipelinePolling', null, { root: true });
- dispatch('pipelines/clearEtagPoll', null, { root: true });
dispatch('pipelines/resetLatestPipeline', null, { root: true });
dispatch('setCurrentBranchId', '', { root: true });
+ dispatch('pipelines/stopPipelinePolling', null, { root: true })
+ .then(() => {
+ dispatch('pipelines/clearEtagPoll', null, { root: true });
+ })
+ .catch(e => {
+ throw e;
+ });
router.push(`/project/${projectPath}/merge_requests/${id}`);
};
diff --git a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
index 0a4ea80c4c1..6718f7eae4e 100644
--- a/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
+++ b/app/assets/javascripts/ide/stores/modules/pipelines/actions.js
@@ -12,8 +12,12 @@ let eTagPoll;
export const clearEtagPoll = () => {
eTagPoll = null;
};
-export const stopPipelinePolling = () => eTagPoll && eTagPoll.stop();
-export const restartPipelinePolling = () => eTagPoll && eTagPoll.restart();
+export const stopPipelinePolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+export const restartPipelinePolling = () => {
+ if (eTagPoll) eTagPoll.restart();
+};
export const requestLatestPipeline = ({ commit }) => commit(types.REQUEST_LATEST_PIPELINE);
export const receiveLatestPipelineError = ({ commit, dispatch }) => {
@@ -51,9 +55,9 @@ export const fetchLatestPipeline = ({ dispatch, rootGetters }) => {
Visibility.change(() => {
if (!Visibility.hidden()) {
- eTagPoll.restart();
+ dispatch('restartPipelinePolling');
} else {
- eTagPoll.stop();
+ dispatch('stopPipelinePolling');
}
});
};
diff --git a/app/assets/javascripts/ide/stores/mutations/file.js b/app/assets/javascripts/ide/stores/mutations/file.js
index 13f123b6630..5826f6cb828 100644
--- a/app/assets/javascripts/ide/stores/mutations/file.js
+++ b/app/assets/javascripts/ide/stores/mutations/file.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
import * as types from '../mutation_types';
+import { diffModes } from '../../constants';
export default {
[types.SET_FILE_ACTIVE](state, { path, active }) {
@@ -85,8 +86,19 @@ export default {
});
},
[types.SET_FILE_MERGE_REQUEST_CHANGE](state, { file, mrChange }) {
+ let diffMode = diffModes.replaced;
+ if (mrChange.new_file) {
+ diffMode = diffModes.new;
+ } else if (mrChange.deleted_file) {
+ diffMode = diffModes.deleted;
+ } else if (mrChange.renamed_file) {
+ diffMode = diffModes.renamed;
+ }
Object.assign(state.entries[file.path], {
- mrChange,
+ mrChange: {
+ ...mrChange,
+ diffMode,
+ },
});
},
[types.SET_FILE_VIEWMODE](state, { file, viewMode }) {
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index e0b9766fbee..a04a33cd12d 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -39,7 +39,7 @@ export const dataStructure = () => ({
editorColumn: 1,
fileLanguage: '',
eol: '',
- viewMode: 'edit',
+ viewMode: 'editor',
previewMode: null,
size: 0,
parentPath: null,
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index ae577e04a56..1174177f561 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -110,25 +110,25 @@
<template>
<div
v-if="descriptionHtml"
- class="description"
:class="{
'js-task-list-container': canUpdate
}"
+ class="description"
>
<div
- class="wiki"
+ ref="gfm-content"
:class="{
'issue-realtime-pre-pulse': preAnimation,
'issue-realtime-trigger-pulse': pulseAnimation
}"
- v-html="descriptionHtml"
- ref="gfm-content">
+ class="wiki"
+ v-html="descriptionHtml">
</div>
<textarea
- class="hidden js-task-list-field"
v-if="descriptionText"
v-model="descriptionText"
:data-update-url="updateUrl"
+ class="hidden js-task-list-field"
>
</textarea>
diff --git a/app/assets/javascripts/issue_show/components/edit_actions.vue b/app/assets/javascripts/issue_show/components/edit_actions.vue
index 7ef5e679881..8cb0ab22bfb 100644
--- a/app/assets/javascripts/issue_show/components/edit_actions.vue
+++ b/app/assets/javascripts/issue_show/components/edit_actions.vue
@@ -51,16 +51,16 @@
<template>
<div class="prepend-top-default append-bottom-default clearfix">
<button
- class="btn btn-save float-left"
:class="{ disabled: formState.updateLoading || !isSubmitEnabled }"
- type="submit"
:disabled="formState.updateLoading || !isSubmitEnabled"
+ class="btn btn-save float-left"
+ type="submit"
@click.prevent="updateIssuable">
Save changes
<i
+ v-if="formState.updateLoading"
class="fa fa-spinner fa-spin"
- aria-hidden="true"
- v-if="formState.updateLoading">
+ aria-hidden="true">
</i>
</button>
<button
@@ -71,16 +71,16 @@
</button>
<button
v-if="shouldShowDeleteButton"
- class="btn btn-danger float-right append-right-default"
:class="{ disabled: deleteLoading }"
- type="button"
:disabled="deleteLoading"
+ class="btn btn-danger float-right append-right-default"
+ type="button"
@click="deleteIssuable">
Delete
<i
+ v-if="deleteLoading"
class="fa fa-spinner fa-spin"
- aria-hidden="true"
- v-if="deleteLoading">
+ aria-hidden="true">
</i>
</button>
</div>
diff --git a/app/assets/javascripts/issue_show/components/edited.vue b/app/assets/javascripts/issue_show/components/edited.vue
index 01097b5b35e..5ff5b1630b1 100644
--- a/app/assets/javascripts/issue_show/components/edited.vue
+++ b/app/assets/javascripts/issue_show/components/edited.vue
@@ -37,16 +37,16 @@
Edited
<time-ago-tooltip
v-if="updatedAt"
- tooltip-placement="bottom"
:time="updatedAt"
+ tooltip-placement="bottom"
/>
<span
v-if="hasUpdatedBy"
>
by
<a
- class="author_link"
:href="updatedByPath"
+ class="author_link"
>
<span>{{ updatedByName }}</span>
</a>
diff --git a/app/assets/javascripts/issue_show/components/fields/description.vue b/app/assets/javascripts/issue_show/components/fields/description.vue
index d9fa2764d65..5f58f671c73 100644
--- a/app/assets/javascripts/issue_show/components/fields/description.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description.vue
@@ -52,13 +52,13 @@
>
<textarea
id="issue-description"
+ ref="textarea"
+ slot="textarea"
+ v-model="formState.description"
class="note-textarea js-gfm-input js-autosize markdown-area"
data-supports-quick-actions="false"
aria-label="Description"
- v-model="formState.description"
- ref="textarea"
- slot="textarea"
- placeholder="Write a comment or drag your files here..."
+ placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="updateIssuable"
@keydown.ctrl.enter="updateIssuable">
</textarea>
diff --git a/app/assets/javascripts/issue_show/components/fields/description_template.vue b/app/assets/javascripts/issue_show/components/fields/description_template.vue
index 7db0488e306..e90d9fad94e 100644
--- a/app/assets/javascripts/issue_show/components/fields/description_template.vue
+++ b/app/assets/javascripts/issue_show/components/fields/description_template.vue
@@ -48,15 +48,15 @@
class="dropdown js-issuable-selector-wrap"
data-issuable-type="issue">
<button
+ ref="toggle"
+ :data-namespace-path="projectNamespace"
+ :data-project-path="projectPath"
+ :data-data="issuableTemplatesJson"
class="dropdown-menu-toggle js-issuable-selector"
type="button"
- ref="toggle"
data-field-name="issuable_template"
data-selected="null"
- data-toggle="dropdown"
- :data-namespace-path="projectNamespace"
- :data-project-path="projectPath"
- :data-data="issuableTemplatesJson">
+ data-toggle="dropdown">
<span class="dropdown-toggle-text">
Choose a template
</span>
diff --git a/app/assets/javascripts/issue_show/components/fields/title.vue b/app/assets/javascripts/issue_show/components/fields/title.vue
index c3abb9fd9d5..7d1526a64b4 100644
--- a/app/assets/javascripts/issue_show/components/fields/title.vue
+++ b/app/assets/javascripts/issue_show/components/fields/title.vue
@@ -21,11 +21,11 @@
</label>
<input
id="issuable-title"
+ v-model="formState.title"
class="form-control"
type="text"
placeholder="Title"
aria-label="Title"
- v-model="formState.title"
@keydown.meta.enter="updateIssuable"
@keydown.ctrl.enter="updateIssuable" />
</fieldset>
diff --git a/app/assets/javascripts/issue_show/components/form.vue b/app/assets/javascripts/issue_show/components/form.vue
index ab8bd34762f..5bfc072e3da 100644
--- a/app/assets/javascripts/issue_show/components/form.vue
+++ b/app/assets/javascripts/issue_show/components/form.vue
@@ -72,8 +72,8 @@
<locked-warning v-if="formState.lockedWarningVisible" />
<div class="row">
<div
- class="col-sm-4 col-lg-3"
- v-if="hasIssuableTemplates">
+ v-if="hasIssuableTemplates"
+ class="col-sm-4 col-lg-3">
<description-template
:form-state="formState"
:issuable-templates="issuableTemplates"
diff --git a/app/assets/javascripts/issue_show/components/title.vue b/app/assets/javascripts/issue_show/components/title.vue
index aec890a2ff6..12101c0daa5 100644
--- a/app/assets/javascripts/issue_show/components/title.vue
+++ b/app/assets/javascripts/issue_show/components/title.vue
@@ -67,11 +67,11 @@
<template>
<div class="title-container">
<h2
- class="title"
:class="{
'issue-realtime-pre-pulse': preAnimation,
'issue-realtime-trigger-pulse': pulseAnimation
}"
+ class="title"
v-html="titleHtml"
>
</h2>
@@ -80,11 +80,11 @@
v-if="showInlineEditButton && canUpdate"
type="button"
class="btn btn-default btn-edit btn-svg js-issuable-edit"
- v-html="pencilIcon"
title="Edit title and description"
data-placement="bottom"
data-container="body"
@click="edit"
+ v-html="pencilIcon"
>
</button>
</div>
diff --git a/app/assets/javascripts/jobs/components/header.vue b/app/assets/javascripts/jobs/components/header.vue
index 5704d753277..1e7f4b2c3f7 100644
--- a/app/assets/javascripts/jobs/components/header.vue
+++ b/app/assets/javascripts/jobs/components/header.vue
@@ -74,13 +74,13 @@ export default {
<ci-header
v-if="shouldRenderContent"
:status="status"
- item-name="Job"
:item-id="job.id"
:time="headerTime"
:user="job.user"
:actions="actions"
:has-sidebar-button="true"
:should-render-triggered-label="jobStarted"
+ item-name="Job"
/>
<loading-icon
v-if="isLoading"
diff --git a/app/assets/javascripts/jobs/components/sidebar_details_block.vue b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
index 8f3c66b0cbe..d2adf628050 100644
--- a/app/assets/javascripts/jobs/components/sidebar_details_block.vue
+++ b/app/assets/javascripts/jobs/components/sidebar_details_block.vue
@@ -101,8 +101,8 @@ export default {
{{ __('Retry') }}
</a>
<button
- type="button"
:aria-label="__('Toggle Sidebar')"
+ type="button"
class="btn btn-blank gutter-toggle float-right d-block d-md-none js-sidebar-build-toggle"
>
<i
@@ -114,20 +114,20 @@ export default {
</div>
<template v-if="shouldRenderContent">
<div
- class="block retry-link"
v-if="job.retry_path || job.new_issue_path"
+ class="block retry-link"
>
<a
v-if="job.new_issue_path"
- class="js-new-issue btn btn-new btn-inverted"
:href="job.new_issue_path"
+ class="js-new-issue btn btn-new btn-inverted"
>
{{ __('New issue') }}
</a>
<a
v-if="canUserRetry"
- class="js-retry-job btn btn-inverted-secondary"
:href="job.retry_path"
+ class="js-retry-job btn btn-inverted-secondary"
data-method="post"
rel="nofollow"
>
@@ -136,8 +136,8 @@ export default {
</div>
<div :class="{block : renderBlock }">
<p
- class="build-detail-row js-job-mr"
v-if="job.merge_request"
+ class="build-detail-row js-job-mr"
>
<span class="build-light-text">
{{ __('Merge Request:') }}
@@ -148,51 +148,51 @@ export default {
</p>
<detail-row
- class="js-job-duration"
v-if="job.duration"
- title="Duration"
:value="duration"
+ class="js-job-duration"
+ title="Duration"
/>
<detail-row
- class="js-job-finished"
v-if="job.finished_at"
- title="Finished"
:value="timeFormated(job.finished_at)"
+ class="js-job-finished"
+ title="Finished"
/>
<detail-row
- class="js-job-erased"
v-if="job.erased_at"
- title="Erased"
:value="timeFormated(job.erased_at)"
+ class="js-job-erased"
+ title="Erased"
/>
<detail-row
- class="js-job-queued"
v-if="job.queued"
- title="Queued"
:value="queued"
+ class="js-job-queued"
+ title="Queued"
/>
<detail-row
- class="js-job-timeout"
v-if="hasTimeout"
- title="Timeout"
:help-url="runnerHelpUrl"
:value="timeout"
+ class="js-job-timeout"
+ title="Timeout"
/>
<detail-row
- class="js-job-runner"
v-if="job.runner"
- title="Runner"
:value="runnerId"
+ class="js-job-runner"
+ title="Runner"
/>
<detail-row
- class="js-job-coverage"
v-if="job.coverage"
- title="Coverage"
:value="coverage"
+ class="js-job-coverage"
+ title="Coverage"
/>
<p
- class="build-detail-row js-job-tags"
v-if="job.tags.length"
+ class="build-detail-row js-job-tags"
>
<span class="build-light-text">
{{ __('Tags:') }}
@@ -210,8 +210,8 @@ export default {
class="btn-group prepend-top-5"
role="group">
<a
- class="js-cancel-job btn btn-sm btn-default"
:href="job.cancel_path"
+ class="js-cancel-job btn btn-sm btn-default"
data-method="post"
rel="nofollow"
>
@@ -221,8 +221,8 @@ export default {
</div>
</template>
<loading-icon
- class="prepend-top-10"
v-if="isLoading"
+ class="prepend-top-10"
size="2"
/>
</div>
diff --git a/app/assets/javascripts/label_manager.js b/app/assets/javascripts/label_manager.js
index 8c3de6e4045..8b01024b7d4 100644
--- a/app/assets/javascripts/label_manager.js
+++ b/app/assets/javascripts/label_manager.js
@@ -13,6 +13,7 @@ export default class LabelManager {
this.otherLabels = otherLabels || $('.js-other-labels');
this.errorMessage = 'Unable to update label prioritization at this time';
this.emptyState = document.querySelector('#js-priority-labels-empty-state');
+ this.$badgeItemTemplate = $('#js-badge-item-template');
this.sortable = Sortable.create(this.prioritizedLabels.get(0), {
filter: '.empty-message',
forceFallback: true,
@@ -63,7 +64,11 @@ export default class LabelManager {
$target = this.otherLabels;
$from = this.prioritizedLabels;
}
- $label.detach().appendTo($target);
+
+ const $detachedLabel = $label.detach();
+ this.toggleLabelPriorityBadge($detachedLabel, action);
+ $detachedLabel.appendTo($target);
+
if ($from.find('li').length) {
$from.find('.empty-message').removeClass('hidden');
}
@@ -88,6 +93,14 @@ export default class LabelManager {
}
}
+ toggleLabelPriorityBadge($label, action) {
+ if (action === 'remove') {
+ $('.js-priority-badge', $label).remove();
+ } else {
+ $('.label-links', $label).append(this.$badgeItemTemplate.clone().html());
+ }
+ }
+
onPrioritySortUpdate() {
this.savePrioritySort()
.catch(() => flash(this.errorMessage));
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 8b5445d012b..d55d0585031 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -384,6 +384,49 @@ export const backOff = (fn, timeout = 60000) => {
});
};
+export const createOverlayIcon = (iconPath, overlayPath) => {
+ const faviconImage = document.createElement('img');
+
+ return new Promise((resolve) => {
+ faviconImage.onload = () => {
+ const size = 32;
+
+ const canvas = document.createElement('canvas');
+ canvas.width = size;
+ canvas.height = size;
+
+ const context = canvas.getContext('2d');
+ context.clearRect(0, 0, size, size);
+ context.drawImage(
+ faviconImage, 0, 0, faviconImage.width, faviconImage.height, 0, 0, size, size,
+ );
+
+ const overlayImage = document.createElement('img');
+ overlayImage.onload = () => {
+ context.drawImage(
+ overlayImage, 0, 0, overlayImage.width, overlayImage.height, 0, 0, size, size,
+ );
+
+ const faviconWithOverlayUrl = canvas.toDataURL();
+
+ resolve(faviconWithOverlayUrl);
+ };
+ overlayImage.src = overlayPath;
+ };
+ faviconImage.src = iconPath;
+ });
+};
+
+export const setFaviconOverlay = (overlayPath) => {
+ const faviconEl = document.getElementById('favicon');
+
+ if (!faviconEl) { return null; }
+
+ const iconPath = faviconEl.getAttribute('data-original-href');
+
+ return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl => faviconEl.setAttribute('href', faviconWithOverlayUrl));
+};
+
export const setFavicon = (faviconPath) => {
const faviconEl = document.getElementById('favicon');
if (faviconEl && faviconPath) {
@@ -393,8 +436,9 @@ export const setFavicon = (faviconPath) => {
export const resetFavicon = () => {
const faviconEl = document.getElementById('favicon');
- const originalFavicon = faviconEl ? faviconEl.getAttribute('href') : null;
+
if (faviconEl) {
+ const originalFavicon = faviconEl.getAttribute('data-original-href');
faviconEl.setAttribute('href', originalFavicon);
}
};
@@ -403,10 +447,9 @@ export const setCiStatusFavicon = pageUrl =>
axios.get(pageUrl)
.then(({ data }) => {
if (data && data.favicon) {
- setFavicon(data.favicon);
- } else {
- resetFavicon();
+ return setFaviconOverlay(data.favicon);
}
+ return resetFavicon();
})
.catch(resetFavicon);
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 0ff23bbb061..7cca32dc6fa 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -79,37 +79,37 @@ export function getTimeago() {
if (!timeagoInstance) {
const localeRemaining = function getLocaleRemaining(number, index) {
return [
- [s__('Timeago|less than a minute ago'), s__('Timeago|right now')],
- [s__('Timeago|less than a minute ago'), s__('Timeago|%s seconds remaining')],
- [s__('Timeago|about a minute ago'), s__('Timeago|1 minute remaining')],
+ [s__('Timeago|just now'), s__('Timeago|right now')],
+ [s__('Timeago|%s seconds ago'), s__('Timeago|%s seconds remaining')],
+ [s__('Timeago|1 minute ago'), s__('Timeago|1 minute remaining')],
[s__('Timeago|%s minutes ago'), s__('Timeago|%s minutes remaining')],
- [s__('Timeago|about an hour ago'), s__('Timeago|1 hour remaining')],
- [s__('Timeago|about %s hours ago'), s__('Timeago|%s hours remaining')],
- [s__('Timeago|a day ago'), s__('Timeago|1 day remaining')],
+ [s__('Timeago|1 hour ago'), s__('Timeago|1 hour remaining')],
+ [s__('Timeago|%s hours ago'), s__('Timeago|%s hours remaining')],
+ [s__('Timeago|1 day ago'), s__('Timeago|1 day remaining')],
[s__('Timeago|%s days ago'), s__('Timeago|%s days remaining')],
- [s__('Timeago|a week ago'), s__('Timeago|1 week remaining')],
+ [s__('Timeago|1 week ago'), s__('Timeago|1 week remaining')],
[s__('Timeago|%s weeks ago'), s__('Timeago|%s weeks remaining')],
- [s__('Timeago|a month ago'), s__('Timeago|1 month remaining')],
+ [s__('Timeago|1 month ago'), s__('Timeago|1 month remaining')],
[s__('Timeago|%s months ago'), s__('Timeago|%s months remaining')],
- [s__('Timeago|a year ago'), s__('Timeago|1 year remaining')],
+ [s__('Timeago|1 year ago'), s__('Timeago|1 year remaining')],
[s__('Timeago|%s years ago'), s__('Timeago|%s years remaining')],
][index];
};
const locale = function getLocale(number, index) {
return [
- [s__('Timeago|less than a minute ago'), s__('Timeago|right now')],
- [s__('Timeago|less than a minute ago'), s__('Timeago|in %s seconds')],
- [s__('Timeago|about a minute ago'), s__('Timeago|in 1 minute')],
+ [s__('Timeago|just now'), s__('Timeago|right now')],
+ [s__('Timeago|%s seconds ago'), s__('Timeago|in %s seconds')],
+ [s__('Timeago|1 minute ago'), s__('Timeago|in 1 minute')],
[s__('Timeago|%s minutes ago'), s__('Timeago|in %s minutes')],
- [s__('Timeago|about an hour ago'), s__('Timeago|in 1 hour')],
- [s__('Timeago|about %s hours ago'), s__('Timeago|in %s hours')],
- [s__('Timeago|a day ago'), s__('Timeago|in 1 day')],
+ [s__('Timeago|1 hour ago'), s__('Timeago|in 1 hour')],
+ [s__('Timeago|%s hours ago'), s__('Timeago|in %s hours')],
+ [s__('Timeago|1 day ago'), s__('Timeago|in 1 day')],
[s__('Timeago|%s days ago'), s__('Timeago|in %s days')],
- [s__('Timeago|a week ago'), s__('Timeago|in 1 week')],
+ [s__('Timeago|1 week ago'), s__('Timeago|in 1 week')],
[s__('Timeago|%s weeks ago'), s__('Timeago|in %s weeks')],
- [s__('Timeago|a month ago'), s__('Timeago|in 1 month')],
+ [s__('Timeago|1 month ago'), s__('Timeago|in 1 month')],
[s__('Timeago|%s months ago'), s__('Timeago|in %s months')],
- [s__('Timeago|a year ago'), s__('Timeago|in 1 year')],
+ [s__('Timeago|1 year ago'), s__('Timeago|in 1 year')],
[s__('Timeago|%s years ago'), s__('Timeago|in %s years')],
][index];
};
@@ -270,6 +270,17 @@ export const totalDaysInMonth = date => {
};
/**
+ * Returns number of days in a quarter from provided
+ * months array.
+ *
+ * @param {Array} quarter
+ */
+export const totalDaysInQuarter = quarter => quarter.reduce(
+ (acc, month) => acc + totalDaysInMonth(month),
+ 0,
+);
+
+/**
* Returns list of Dates referring to Sundays of the month
* based on provided date
*
@@ -309,42 +320,27 @@ export const getSundays = date => {
};
/**
- * Returns list of Dates representing a timeframe of Months from month of provided date (inclusive)
- * up to provided length
- *
- * For eg;
- * If current month is January 2018 and `length` provided is `6`
- * Then this method will return list of Date objects as follows;
- *
- * [ October 2017, November 2017, December 2017, January 2018, February 2018, March 2018 ]
- *
- * If current month is March 2018 and `length` provided is `3`
- * Then this method will return list of Date objects as follows;
- *
- * [ February 2018, March 2018, April 2018 ]
+ * Returns list of Dates representing a timeframe of months from startDate and length
*
+ * @param {Date} startDate
* @param {Number} length
- * @param {Date} date
*/
-export const getTimeframeWindow = (length, date) => {
- if (!length) {
+export const getTimeframeWindowFrom = (startDate, length) => {
+ if (!(startDate instanceof Date) || !length) {
return [];
}
- const currentDate = date instanceof Date ? date : new Date();
- const currentMonthIndex = Math.floor(length / 2);
- const timeframe = [];
-
- // Move date object backward to the first month of timeframe
- currentDate.setDate(1);
- currentDate.setMonth(currentDate.getMonth() - currentMonthIndex);
-
- // Iterate and update date for the size of length
+ // Iterate and set date for the size of length
// and push date reference to timeframe list
- for (let i = 0; i < length; i += 1) {
- timeframe.push(new Date(currentDate.getTime()));
- currentDate.setMonth(currentDate.getMonth() + 1);
- }
+ const timeframe = new Array(length)
+ .fill()
+ .map(
+ (val, i) => new Date(
+ startDate.getFullYear(),
+ startDate.getMonth() + i,
+ 1,
+ ),
+ );
// Change date of last timeframe item to last date of the month
timeframe[length - 1].setDate(totalDaysInMonth(timeframe[length - 1]));
@@ -352,6 +348,29 @@ export const getTimeframeWindow = (length, date) => {
return timeframe;
};
+/**
+ * Returns count of day within current quarter from provided date
+ * and array of months for the quarter
+ *
+ * Eg;
+ * If date is 15 Feb 2018
+ * and quarter is [Jan, Feb, Mar]
+ *
+ * Then 15th Feb is 46th day of the quarter
+ * Where 31 (days in Jan) + 15 (date of Feb).
+ *
+ * @param {Date} date
+ * @param {Array} quarter
+ */
+export const dayInQuarter = (date, quarter) => quarter.reduce((acc, month) => {
+ if (date.getMonth() > month.getMonth()) {
+ return acc + totalDaysInMonth(month);
+ } else if (date.getMonth() === month.getMonth()) {
+ return acc + date.getDate();
+ }
+ return acc + 0;
+}, 0);
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/http_status.js b/app/assets/javascripts/lib/utils/http_status.js
index bb151929431..229d53b18b0 100644
--- a/app/assets/javascripts/lib/utils/http_status.js
+++ b/app/assets/javascripts/lib/utils/http_status.js
@@ -8,4 +8,5 @@ export default {
OK: 200,
MULTIPLE_CHOICES: 300,
BAD_REQUEST: 400,
+ NOT_FOUND: 404,
};
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index dd17544b656..72b72f4247d 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -85,9 +85,9 @@ export function redirectTo(url) {
}
export function webIDEUrl(route = undefined) {
- let returnUrl = `${gon.relative_url_root}/-/ide/`;
+ let returnUrl = `${gon.relative_url_root || ''}/-/ide/`;
if (route) {
- returnUrl += `project${route}`;
+ returnUrl += `project${route.replace(new RegExp(`^${gon.relative_url_root || ''}`), '')}`;
}
return returnUrl;
}
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 9803bebfd10..c9ce838cd48 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -144,6 +144,7 @@ document.addEventListener('DOMContentLoaded', () => {
$body.tooltip({
selector: '.has-tooltip, [data-toggle="tooltip"]',
trigger: 'hover',
+ boundary: 'viewport',
placement(tip, el) {
return $(el).data('placement') || 'bottom';
},
diff --git a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
index 2cb238529aa..e4ed8111824 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
@@ -11,9 +11,18 @@ import { __ } from '~/locale';
global.mergeConflicts.diffFileEditor = Vue.extend({
props: {
- file: Object,
- onCancelDiscardConfirmation: Function,
- onAcceptDiscardConfirmation: Function
+ file: {
+ type: Object,
+ required: true,
+ },
+ onCancelDiscardConfirmation: {
+ type: Function,
+ required: true,
+ },
+ onAcceptDiscardConfirmation: {
+ type: Function,
+ required: true,
+ },
},
data() {
return {
diff --git a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js
index 56d6678e1bd..827cf5f478d 100644
--- a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js
+++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js
@@ -1,14 +1,19 @@
-/* eslint-disable no-param-reassign, comma-dangle */
+/* eslint-disable no-param-reassign */
import Vue from 'vue';
+import actionsMixin from '../mixins/line_conflict_actions';
+import utilsMixin from '../mixins/line_conflict_utils';
-((global) => {
+(global => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.inlineConflictLines = Vue.extend({
+ mixins: [utilsMixin, actionsMixin],
props: {
- file: Object
+ file: {
+ type: Object,
+ required: true,
+ },
},
- mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions],
});
})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
index 0fc4a13450a..57e73e38d88 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
@@ -1,15 +1,20 @@
/* eslint-disable no-param-reassign, comma-dangle */
import Vue from 'vue';
+import actionsMixin from '../mixins/line_conflict_actions';
+import utilsMixin from '../mixins/line_conflict_utils';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
global.mergeConflicts.parallelConflictLines = Vue.extend({
+ mixins: [utilsMixin, actionsMixin],
props: {
- file: Object
+ file: {
+ type: Object,
+ required: true,
+ },
},
- mixins: [global.mergeConflicts.utils, global.mergeConflicts.actions],
template: `
<table>
<tr class="line_holder parallel" v-for="section in file.parallelLines">
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js
index c68b47c9348..64d69159222 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_service.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_service.js
@@ -1,23 +1,16 @@
-/* eslint-disable no-param-reassign, comma-dangle */
import axios from '../lib/utils/axios_utils';
-((global) => {
- global.mergeConflicts = global.mergeConflicts || {};
-
- class mergeConflictsService {
- constructor(options) {
- this.conflictsPath = options.conflictsPath;
- this.resolveConflictsPath = options.resolveConflictsPath;
- }
-
- fetchConflictsData() {
- return axios.get(this.conflictsPath);
- }
+export default class MergeConflictsService {
+ constructor(options) {
+ this.conflictsPath = options.conflictsPath;
+ this.resolveConflictsPath = options.resolveConflictsPath;
+ }
- submitResolveConflicts(data) {
- return axios.post(this.resolveConflictsPath, data);
- }
+ fetchConflictsData() {
+ return axios.get(this.conflictsPath);
}
- global.mergeConflicts.mergeConflictsService = mergeConflictsService;
-})(window.gl || (window.gl = {}));
+ submitResolveConflicts(data) {
+ return axios.post(this.resolveConflictsPath, data);
+ }
+}
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index 4abd5433bb5..326d4523cce 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -5,7 +5,7 @@ import Vue from 'vue';
import Flash from '../flash';
import initIssuableSidebar from '../init_issuable_sidebar';
import './merge_conflict_store';
-import './merge_conflict_service';
+import MergeConflictsService from './merge_conflict_service';
import './mixins/line_conflict_utils';
import './mixins/line_conflict_actions';
import './components/diff_file_editor';
@@ -17,7 +17,7 @@ export default function initMergeConflicts() {
const INTERACTIVE_RESOLVE_MODE = 'interactive';
const conflictsEl = document.querySelector('#conflicts');
const mergeConflictsStore = gl.mergeConflicts.mergeConflictsStore;
- const mergeConflictsService = new gl.mergeConflicts.mergeConflictsService({
+ const mergeConflictsService = new MergeConflictsService({
conflictsPath: conflictsEl.dataset.conflictsPath,
resolveConflictsPath: conflictsEl.dataset.resolveConflictsPath
});
diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js
index 53e000d7e9e..364ae2b2688 100644
--- a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_actions.js
@@ -1,13 +1,7 @@
-/* eslint-disable no-param-reassign, comma-dangle */
-
-((global) => {
- global.mergeConflicts = global.mergeConflicts || {};
-
- global.mergeConflicts.actions = {
- methods: {
- handleSelected(file, sectionId, selection) {
- gl.mergeConflicts.mergeConflictsStore.handleSelected(file, sectionId, selection);
- }
- }
- };
-})(window.gl || (window.gl = {}));
+export default {
+ methods: {
+ handleSelected(file, sectionId, selection) {
+ gl.mergeConflicts.mergeConflictsStore.handleSelected(file, sectionId, selection);
+ },
+ },
+};
diff --git a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js
index 0f475f62ee6..d25032fb142 100644
--- a/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js
+++ b/app/assets/javascripts/merge_conflicts/mixins/line_conflict_utils.js
@@ -1,19 +1,13 @@
-/* eslint-disable no-param-reassign, quote-props, comma-dangle */
-
-((global) => {
- global.mergeConflicts = global.mergeConflicts || {};
-
- global.mergeConflicts.utils = {
- methods: {
- lineCssClass(line) {
- return {
- 'head': line.isHead,
- 'origin': line.isOrigin,
- 'match': line.hasMatch,
- 'selected': line.isSelected,
- 'unselected': line.isUnselected
- };
- }
- }
- };
-})(window.gl || (window.gl = {}));
+export default {
+ methods: {
+ lineCssClass(line) {
+ return {
+ head: line.isHead,
+ origin: line.isOrigin,
+ match: line.hasMatch,
+ selected: line.isSelected,
+ unselected: line.isUnselected,
+ };
+ },
+ },
+};
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 493c119dc6f..65ab41559be 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -362,7 +362,7 @@ export default class MergeRequestTabs {
//
// status - Boolean, true to show, false to hide
toggleLoading(status) {
- $('.mr-loading-status .loading').toggleClass('hidden', !status);
+ $('.mr-loading-status .loading').toggleClass('hide', !status);
}
diffViewType() {
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index f8b3d3061f0..d269c45203a 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -56,7 +56,7 @@ export default class MilestoneSelect {
if (issueUpdateURL) {
milestoneLinkTemplate = _.template(
- '<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>',
+ '<a href="<%- web_url %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>',
);
milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
}
diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue
index c77f451c2d3..82b9a4b1adb 100644
--- a/app/assets/javascripts/monitoring/components/empty_state.vue
+++ b/app/assets/javascripts/monitoring/components/empty_state.vue
@@ -107,8 +107,8 @@ export default {
<div class="state-button">
<a
v-if="currentState.buttonPath"
- class="btn btn-success"
:href="currentState.buttonPath"
+ class="btn btn-success"
>
{{ currentState.buttonText }}
</a>
@@ -116,8 +116,8 @@ export default {
<div class="state-button">
<a
v-if="currentState.secondaryButtonPath"
- class="btn"
:href="currentState.secondaryButtonPath"
+ class="btn"
>
{{ currentState.secondaryButtonText }}
</a>
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index 503ee1ce3d1..20400154100 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -241,16 +241,16 @@ export default {
</div>
</div>
<div
- class="prometheus-svg-container"
:style="paddingBottomRootSvg"
+ class="prometheus-svg-container"
>
<svg
- :viewBox="outerViewBox"
ref="baseSvg"
+ :viewBox="outerViewBox"
>
<g
- class="x-axis"
:transform="axisTransform"
+ class="x-axis"
/>
<g
class="y-axis"
@@ -265,9 +265,9 @@ export default {
:unit-of-display="unitOfDisplay"
/>
<svg
- class="graph-data"
- :viewBox="innerViewBox"
ref="graphData"
+ :viewBox="innerViewBox"
+ class="graph-data"
>
<graph-path
v-for="(path, index) in timeSeries"
@@ -287,11 +287,11 @@ export default {
:graph-height-offset="graphHeightOffset"
/>
<rect
- class="prometheus-graph-overlay"
+ ref="graphOverlay"
:width="(graphWidth - 70)"
:height="(graphHeight - 100)"
+ class="prometheus-graph-overlay"
transform="translate(-5, 20)"
- ref="graphOverlay"
@mousemove="handleMouseOverGraph($event)"
/>
</svg>
diff --git a/app/assets/javascripts/monitoring/components/graph/axis.vue b/app/assets/javascripts/monitoring/components/graph/axis.vue
index fc4b3689dfd..8a604a51eb2 100644
--- a/app/assets/javascripts/monitoring/components/graph/axis.vue
+++ b/app/assets/javascripts/monitoring/components/graph/axis.vue
@@ -92,48 +92,48 @@ export default {
<template>
<g class="axis-label-container">
<line
+ :y1="yPosition"
+ :x2="graphWidth + 20"
+ :y2="yPosition"
class="label-x-axis-line"
stroke="#000000"
stroke-width="1"
x1="10"
- :y1="yPosition"
- :x2="graphWidth + 20"
- :y2="yPosition"
/>
<line
+ :x2="10"
+ :y2="yPosition"
class="label-y-axis-line"
stroke="#000000"
stroke-width="1"
x1="10"
y1="0"
- :x2="10"
- :y2="yPosition"
/>
<rect
- class="rect-axis-text"
:transform="rectTransform"
:width="yLabelWidth"
:height="yLabelHeight"
+ class="rect-axis-text"
/>
<text
+ ref="ylabel"
+ :transform="textTransform"
class="label-axis-text y-label-text"
text-anchor="middle"
- :transform="textTransform"
- ref="ylabel"
>
{{ yAxisLabelSentenceCase }}
</text>
<rect
- class="rect-axis-text"
:x="xPosition + 60"
:y="graphHeight - 80"
+ class="rect-axis-text"
width="35"
height="50"
/>
<text
- class="label-axis-text x-label-text"
:x="xPosition + 60"
:y="yPosition"
+ class="label-axis-text x-label-text"
dy=".35em"
>
{{ timeString }}
diff --git a/app/assets/javascripts/monitoring/components/graph/deployment.vue b/app/assets/javascripts/monitoring/components/graph/deployment.vue
index 4012191ceb9..a7289ed53e8 100644
--- a/app/assets/javascripts/monitoring/components/graph/deployment.vue
+++ b/app/assets/javascripts/monitoring/components/graph/deployment.vue
@@ -33,18 +33,18 @@ export default {
:key="index"
:transform="transformDeploymentGroup(deployment)">
<rect
+ :height="calculatedHeight"
x="0"
y="0"
- :height="calculatedHeight"
width="3"
fill="url(#shadow-gradient)"
/>
<line
+ :y2="calculatedHeight"
class="deployment-line"
x1="0"
y1="0"
x2="0"
- :y2="calculatedHeight"
stroke="#000"
/>
</g>
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 8a771107de8..282c5c24384 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -117,13 +117,13 @@ export default {
<template>
<div
- class="prometheus-graph-cursor"
:style="cursorStyle"
+ class="prometheus-graph-cursor"
>
<div
v-if="showFlagContent"
- class="prometheus-graph-flag popover"
:class="flagOrientation"
+ class="prometheus-graph-flag popover"
>
<div class="arrow"></div>
<div class="popover-title">
@@ -139,8 +139,8 @@ export default {
>
<div>
<icon
- name="commit"
:size="12"
+ name="commit"
/>
<a :href="deploymentFlagData.commitUrl">
{{ deploymentFlagData.sha.slice(0, 8) }}
@@ -150,8 +150,8 @@ export default {
v-if="deploymentFlagData.tag"
>
<icon
- name="label"
:size="12"
+ name="label"
/>
<a :href="deploymentFlagData.tagUrl">
{{ deploymentFlagData.ref }}
diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue
index da9280cf1f1..3276f3a1ceb 100644
--- a/app/assets/javascripts/monitoring/components/graph/legend.vue
+++ b/app/assets/javascripts/monitoring/components/graph/legend.vue
@@ -31,8 +31,8 @@ export default {
<table class="prometheus-table">
<tr
v-for="(series, index) in timeSeries"
- :key="index"
v-if="series.shouldRenderLegend"
+ :key="index"
:class="isStable(series)"
>
<td>
@@ -40,11 +40,11 @@ export default {
</td>
<track-line :track="series" />
<td
- class="legend-metric-title"
- v-if="timeSeries.length > 1">
+ v-if="timeSeries.length > 1"
+ class="legend-metric-title">
<track-info
- :track="series"
- v-if="series.metricTag" />
+ v-if="series.metricTag"
+ :track="series" />
<track-info
v-else
:track="series">
@@ -62,8 +62,8 @@ export default {
:key="`track-line-${trackIndex}`"/>
<td :key="`track-info-${trackIndex}`">
<track-info
- class="legend-metric-title"
- :track="track" />
+ :track="track"
+ class="legend-metric-title" />
</td>
</template>
</tr>
diff --git a/app/assets/javascripts/monitoring/components/graph/path.vue b/app/assets/javascripts/monitoring/components/graph/path.vue
index 52f8aa2ee3f..a9b7ce586ce 100644
--- a/app/assets/javascripts/monitoring/components/graph/path.vue
+++ b/app/assets/javascripts/monitoring/components/graph/path.vue
@@ -44,26 +44,26 @@ export default {
<template>
<g transform="translate(-5, 20)">
<circle
- class="circle-path"
+ v-if="showDot"
:cx="currentCoordinates.currentX"
:cy="currentCoordinates.currentY"
:fill="lineColor"
:stroke="lineColor"
+ class="circle-path"
r="3"
- v-if="showDot"
/>
<path
- class="metric-area"
:d="generatedAreaPath"
:fill="areaColor"
+ class="metric-area"
/>
<path
- class="metric-line"
:d="generatedLinePath"
:stroke="lineColor"
+ :stroke-dasharray="strokeDashArray"
+ class="metric-line"
fill="none"
stroke-width="1"
- :stroke-dasharray="strokeDashArray"
/>
</g>
</template>
diff --git a/app/assets/javascripts/monitoring/components/graph/track_line.vue b/app/assets/javascripts/monitoring/components/graph/track_line.vue
index 18be65fd1ef..ba3f93b39ff 100644
--- a/app/assets/javascripts/monitoring/components/graph/track_line.vue
+++ b/app/assets/javascripts/monitoring/components/graph/track_line.vue
@@ -24,11 +24,11 @@ export default {
<line
:stroke-dasharray="stylizedLine"
:stroke="track.lineColor"
- stroke-width="4"
:x1="0"
:x2="16"
:y1="4"
:y2="4"
+ stroke-width="4"
/>
</svg>
</td>
diff --git a/app/assets/javascripts/notebook/cells/code.vue b/app/assets/javascripts/notebook/cells/code.vue
index b4067d229aa..18cef82cec0 100644
--- a/app/assets/javascripts/notebook/cells/code.vue
+++ b/app/assets/javascripts/notebook/cells/code.vue
@@ -39,10 +39,10 @@ export default {
<template>
<div class="cell">
<code-cell
- type="input"
:raw-code="rawInputCode"
:count="cell.execution_count"
- :code-css-class="codeCssClass" />
+ :code-css-class="codeCssClass"
+ type="input" />
<output-cell
v-if="hasOutput"
:count="cell.execution_count"
diff --git a/app/assets/javascripts/notebook/cells/code/index.vue b/app/assets/javascripts/notebook/cells/code/index.vue
index 0f3083f05b2..7d2a1a33b98 100644
--- a/app/assets/javascripts/notebook/cells/code/index.vue
+++ b/app/assets/javascripts/notebook/cells/code/index.vue
@@ -48,9 +48,9 @@
:type="promptType"
:count="count" />
<pre
- class="language-python"
- :class="codeCssClass"
ref="code"
+ :class="codeCssClass"
+ class="language-python"
v-text="code">
</pre>
</div>
diff --git a/app/assets/javascripts/notebook/cells/output/index.vue b/app/assets/javascripts/notebook/cells/output/index.vue
index 91b2269a83a..4183b976814 100644
--- a/app/assets/javascripts/notebook/cells/output/index.vue
+++ b/app/assets/javascripts/notebook/cells/output/index.vue
@@ -78,10 +78,10 @@
<template>
<component
:is="componentName"
- type="output"
:output-type="outputType"
:count="count"
:raw-code="rawCode"
:code-css-class="codeCssClass"
+ type="output"
/>
</template>
diff --git a/app/assets/javascripts/notes/components/comment_form.vue b/app/assets/javascripts/notes/components/comment_form.vue
index 72d7e22fba0..ad6dd3d9a09 100644
--- a/app/assets/javascripts/notes/components/comment_form.vue
+++ b/app/assets/javascripts/notes/components/comment_form.vue
@@ -312,8 +312,8 @@ Please check your network connection and try again.`;
<div>
<note-signed-out-widget v-if="!isLoggedIn" />
<discussion-locked-widget
- issuable-type="issue"
v-else-if="isLocked(getNoteableData) && !canCreateNote"
+ issuable-type="issue"
/>
<ul
v-else-if="canCreateNote"
@@ -345,23 +345,23 @@ Please check your network connection and try again.`;
/>
<markdown-field
+ ref="markdownField"
:markdown-preview-path="markdownPreviewPath"
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
- :add-spacing-classes="false"
- ref="markdownField">
+ :add-spacing-classes="false">
<textarea
id="note-body"
+ ref="textarea"
+ slot="textarea"
+ v-model="note"
+ :disabled="isSubmitting"
name="note[note]"
class="note-textarea js-vue-comment-form
js-gfm-input js-autosize markdown-area js-vue-textarea"
data-supports-quick-actions="true"
aria-label="Description"
- v-model="note"
- ref="textarea"
- slot="textarea"
- :disabled="isSubmitting"
- placeholder="Write a comment or drag your files here..."
+ placeholder="Write a comment or drag your files here…"
@keydown.up="editCurrentUserLastNote()"
@keydown.meta.enter="handleSave()"
@keydown.ctrl.enter="handleSave()">
@@ -372,10 +372,10 @@ js-gfm-input js-autosize markdown-area js-vue-textarea"
class="float-left btn-group
append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown">
<button
- @click.prevent="handleSave()"
:disabled="isSubmitButtonDisabled"
class="btn btn-create comment-btn js-comment-button js-comment-submit-button"
- type="submit">
+ type="submit"
+ @click.prevent="handleSave()">
{{ __(commentButtonTitle) }}
</button>
<button
@@ -434,20 +434,20 @@ append-right-10 comment-type-dropdown js-comment-type-dropdown droplab-dropdown"
<loading-button
v-if="canUpdateIssue"
:loading="isToggleStateButtonLoading"
- @click="handleSave(true)"
:container-class="[
actionButtonClassNames,
'btn btn-comment btn-comment-and-close js-action-button'
]"
:disabled="isToggleStateButtonLoading || isSubmitting"
:label="issueActionButtonTitle"
+ @click="handleSave(true)"
/>
<button
- type="button"
v-if="note.length"
- @click="discard"
- class="btn btn-cancel js-note-discard">
+ type="button"
+ class="btn btn-cancel js-note-discard"
+ @click="discard">
Discard draft
</button>
</div>
diff --git a/app/assets/javascripts/notes/components/diff_file_header.vue b/app/assets/javascripts/notes/components/diff_file_header.vue
index 94d9dc69964..fc7b52be241 100644
--- a/app/assets/javascripts/notes/components/diff_file_header.vue
+++ b/app/assets/javascripts/notes/components/diff_file_header.vue
@@ -29,12 +29,12 @@ export default {
<span>
<icon name="archive" />
<strong
- v-html="diffFile.submoduleLink"
class="file-title-name"
+ v-html="diffFile.submoduleLink"
></strong>
<clipboard-button
- title="Copy file path to clipboard"
:text="diffFile.submoduleLink"
+ title="Copy file path to clipboard"
css-class="btn-default btn-transparent btn-clipboard"
/>
</span>
@@ -48,16 +48,16 @@ export default {
<span v-html="diffFile.blobIcon"></span>
<span v-if="diffFile.renamedFile">
<strong
- class="file-title-name has-tooltip"
:title="diffFile.oldPath"
+ class="file-title-name has-tooltip"
data-container="body"
>
{{ diffFile.oldPath }}
</strong>
&rarr;
<strong
- class="file-title-name has-tooltip"
:title="diffFile.newPath"
+ class="file-title-name has-tooltip"
data-container="body"
>
{{ diffFile.newPath }}
@@ -66,8 +66,8 @@ export default {
<strong
v-else
- class="file-title-name has-tooltip"
:title="diffFile.oldPath"
+ class="file-title-name has-tooltip"
data-container="body"
>
{{ diffFile.filePath }}
@@ -78,8 +78,8 @@ export default {
</component>
<clipboard-button
- title="Copy file path to clipboard"
:text="diffFile.filePath"
+ title="Copy file path to clipboard"
css-class="btn-default btn-transparent btn-clipboard"
/>
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index ee01ec85bbb..cafb28910eb 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -60,8 +60,8 @@ export default {
<template>
<div
ref="fileHolder"
- class="diff-file file-holder"
:class="diffFileClass"
+ class="diff-file file-holder"
>
<div class="js-file-title file-title file-title-flex-parent">
<diff-file-header
@@ -74,11 +74,11 @@ export default {
>
<table>
<component
+ v-for="(html, index) in diffRows"
:is="rowTag(html)"
:class="html.className"
- v-for="(html, index) in diffRows"
- v-html="html.outerHTML"
:key="index"
+ v-html="html.outerHTML"
/>
<tr class="notes_holder">
<td
diff --git a/app/assets/javascripts/notes/components/discussion_counter.vue b/app/assets/javascripts/notes/components/discussion_counter.vue
index cbe4774a360..68e17ac8055 100644
--- a/app/assets/javascripts/notes/components/discussion_counter.vue
+++ b/app/assets/javascripts/notes/components/discussion_counter.vue
@@ -95,8 +95,8 @@ export default {
class="btn-group"
role="group">
<a
- :href="resolveAllDiscussionsIssuePath"
v-tooltip
+ :href="resolveAllDiscussionsIssuePath"
title="Resolve all discussions in new issue"
data-container="body"
class="new-issue-for-discussion btn btn-default discussion-create-issue-btn">
@@ -108,11 +108,11 @@ export default {
class="btn-group"
role="group">
<button
- @click="jumpToFirstDiscussion"
v-tooltip
title="Jump to first unresolved discussion"
data-container="body"
- class="btn btn-default discussion-next-btn">
+ class="btn btn-default discussion-next-btn"
+ @click="jumpToFirstDiscussion">
<span v-html="nextDiscussionSvg"></span>
</button>
</div>
diff --git a/app/assets/javascripts/notes/components/discussion_locked_widget.vue b/app/assets/javascripts/notes/components/discussion_locked_widget.vue
index 13283b187d1..de0a5f8489b 100644
--- a/app/assets/javascripts/notes/components/discussion_locked_widget.vue
+++ b/app/assets/javascripts/notes/components/discussion_locked_widget.vue
@@ -14,8 +14,8 @@ export default {
<div class="disabled-comment text-center">
<span class="issuable-note-warning inline">
<icon
- name="lock"
:size="16"
+ name="lock"
class="icon"
/>
<span>
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index 626b0799581..0bf4258a257 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -129,12 +129,12 @@ export default {
class="note-actions-item">
<button
v-tooltip
- @click="onResolve"
:class="{ 'is-disabled': !resolvable, 'is-active': isResolved }"
:title="resolveButtonTitle"
:aria-label="resolveButtonTitle"
type="button"
- class="line-resolve-btn note-action-button">
+ class="line-resolve-btn note-action-button"
+ @click="onResolve">
<template v-if="!isResolving">
<div
v-if="isResolved"
@@ -164,16 +164,16 @@ export default {
>
<loading-icon :inline="true" />
<span
- v-html="emojiSmiling"
- class="link-highlight award-control-icon-neutral">
+ class="link-highlight award-control-icon-neutral"
+ v-html="emojiSmiling">
</span>
<span
- v-html="emojiSmiley"
- class="link-highlight award-control-icon-positive">
+ class="link-highlight award-control-icon-positive"
+ v-html="emojiSmiley">
</span>
<span
- v-html="emojiSmile"
- class="link-highlight award-control-icon-super-positive">
+ class="link-highlight award-control-icon-super-positive"
+ v-html="emojiSmile">
</span>
</a>
</div>
@@ -181,16 +181,16 @@ export default {
v-if="canEdit"
class="note-actions-item">
<button
- @click="onEdit"
v-tooltip
type="button"
title="Edit comment"
class="note-action-button js-note-edit btn btn-transparent"
data-container="body"
- data-placement="bottom">
+ data-placement="bottom"
+ @click="onEdit">
<span
- v-html="editSvg"
- class="link-highlight">
+ class="link-highlight"
+ v-html="editSvg">
</span>
</button>
</div>
@@ -218,9 +218,9 @@ export default {
</li>
<li v-if="canEdit">
<button
- @click.prevent="onDelete"
class="btn btn-transparent js-note-delete js-note-delete"
- type="button">
+ type="button"
+ @click.prevent="onDelete">
<span class="text-danger">
Delete comment
</span>
diff --git a/app/assets/javascripts/notes/components/note_awards_list.vue b/app/assets/javascripts/notes/components/note_awards_list.vue
index e8fd155a1ee..521b4d16286 100644
--- a/app/assets/javascripts/notes/components/note_awards_list.vue
+++ b/app/assets/javascripts/notes/components/note_awards_list.vue
@@ -199,10 +199,10 @@ export default {
:key="index"
:class="getAwardClassBindings(awardList, awardName)"
:title="awardTitle(awardList)"
- @click="handleAward(awardName)"
class="btn award-control"
data-placement="bottom"
- type="button">
+ type="button"
+ @click="handleAward(awardName)">
<span v-html="getAwardHTML(awardName)"></span>
<span class="award-control-text js-counter">
{{ awardList.length }}
@@ -220,16 +220,16 @@ export default {
data-placement="bottom"
type="button">
<span
- v-html="emojiSmiling"
- class="award-control-icon award-control-icon-neutral">
+ class="award-control-icon award-control-icon-neutral"
+ v-html="emojiSmiling">
</span>
<span
- v-html="emojiSmiley"
- class="award-control-icon award-control-icon-positive">
+ class="award-control-icon award-control-icon-positive"
+ v-html="emojiSmiley">
</span>
<span
- v-html="emojiSmile"
- class="award-control-icon award-control-icon-super-positive">
+ class="award-control-icon award-control-icon-super-positive"
+ v-html="emojiSmile">
</span>
<i
aria-hidden="true"
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index 0cb626c14f4..864edcd2ec6 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -80,20 +80,20 @@ export default {
<template>
<div
- :class="{ 'js-task-list-container': canEdit }"
ref="note-body"
+ :class="{ 'js-task-list-container': canEdit }"
class="note-body">
<div
- v-html="note.note_html"
- class="note-text md"></div>
+ class="note-text md"
+ v-html="note.note_html"></div>
<note-form
v-if="isEditing"
ref="noteForm"
- @handleFormUpdate="handleFormUpdate"
- @cancelFormEdition="formCancelHandler"
:is-editing="isEditing"
:note-body="noteBody"
:note-id="note.id"
+ @handleFormUpdate="handleFormUpdate"
+ @cancelFormEdition="formCancelHandler"
/>
<textarea
v-if="canEdit"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index c59a2e7a406..7254ef3357d 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -165,15 +165,15 @@ export default {
:add-spacing-classes="false">
<textarea
id="note_note"
+ ref="textarea"
+ slot="textarea"
+ :data-supports-quick-actions="!isEditing"
+ v-model="updatedNoteBody"
name="note[note]"
class="note-textarea js-gfm-input
js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
- :data-supports-quick-actions="!isEditing"
aria-label="Description"
- v-model="updatedNoteBody"
- ref="textarea"
- slot="textarea"
- placeholder="Write a comment or drag your files here..."
+ placeholder="Write a comment or drag your files here…"
@keydown.meta.enter="handleUpdate()"
@keydown.ctrl.enter="handleUpdate()"
@keydown.up="editMyLastNote()"
@@ -182,23 +182,23 @@ js-autosize markdown-area js-vue-issue-note-form js-vue-textarea"
</markdown-field>
<div class="note-form-actions clearfix">
<button
- type="button"
- @click="handleUpdate()"
:disabled="isDisabled"
- class="js-vue-issue-save btn btn-save">
+ type="button"
+ class="js-vue-issue-save btn btn-save"
+ @click="handleUpdate()">
{{ saveButtonTitle }}
</button>
<button
v-if="note.resolvable"
- @click.prevent="handleUpdate(true)"
class="btn btn-nr btn-default append-right-10 js-comment-resolve-button"
+ @click.prevent="handleUpdate(true)"
>
{{ resolveButtonTitle }}
</button>
<button
- @click="cancelHandler()"
class="btn btn-cancel note-edit-cancel"
- type="button">
+ type="button"
+ @click="cancelHandler()">
Cancel
</button>
</div>
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index a4081957207..ffe3ba9c805 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -66,9 +66,9 @@ export default {
v-if="includeToggle"
class="discussion-actions">
<button
- @click="handleToggle"
class="note-action-button discussion-toggle-button js-vue-toggle-button"
- type="button">
+ type="button"
+ @click="handleToggle">
<i
:class="toggleChevronClass"
class="fa"
@@ -90,16 +90,16 @@ export default {
</template>
<span
v-if="actionTextHtml"
- v-html="actionTextHtml"
- class="system-note-message">
+ class="system-note-message"
+ v-html="actionTextHtml">
</span>
<span class="system-note-separator">
&middot;
</span>
<a
:href="noteTimestampLink"
- @click="updateTargetNoteHash"
- class="note-timestamp system-note-separator">
+ class="note-timestamp system-note-separator"
+ @click="updateTargetNoteHash">
<time-ago-tooltip
:time="createdAt"
tooltip-placement="bottom"
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 7f5aacaa3a2..f9f5041a9f9 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -229,9 +229,9 @@ Please check your network connection and try again.`;
:note-id="discussion.id"
:include-toggle="true"
:expanded="note.expanded"
- @toggleHandler="toggleDiscussionHandler"
action-text="started a discussion"
class="discussion"
+ @toggleHandler="toggleDiscussionHandler"
/>
<note-edited-text
v-if="lastUpdatedAt"
@@ -269,19 +269,19 @@ Please check your network connection and try again.`;
class="btn-group"
role="group">
<button
- @click="showReplyForm"
type="button"
class="js-vue-discussion-reply btn btn-text-field"
- title="Add a reply">Reply...</button>
+ title="Add a reply"
+ @click="showReplyForm">Reply...</button>
</div>
<div
v-if="note.resolvable"
class="btn-group"
role="group">
<button
- @click="resolveHandler()"
type="button"
class="btn btn-default"
+ @click="resolveHandler()"
>
<i
v-if="isResolving"
@@ -301,8 +301,8 @@ Please check your network connection and try again.`;
class="btn-group"
role="group">
<a
- :href="note.resolve_with_issue_path"
v-tooltip
+ :href="note.resolve_with_issue_path"
class="new-issue-for-discussion btn
btn-default discussion-create-issue-btn"
title="Resolve this discussion in a new issue"
@@ -316,11 +316,11 @@ Please check your network connection and try again.`;
class="btn-group"
role="group">
<button
- @click="jumpToDiscussion"
v-tooltip
class="btn btn-default discussion-next-btn"
title="Jump to next unresolved discussion"
data-container="body"
+ @click="jumpToDiscussion"
>
<span v-html="nextDiscussionsSvg"></span>
</button>
@@ -330,12 +330,12 @@ Please check your network connection and try again.`;
</template>
<note-form
v-if="isReplying"
- save-button-title="Comment"
+ ref="noteForm"
:note="note"
:is-editing="false"
+ save-button-title="Comment"
@handleFormUpdate="saveReply"
- @cancelFormEdition="cancelReplyForm"
- ref="noteForm" />
+ @cancelFormEdition="cancelReplyForm" />
<note-signed-out-widget v-if="!canReply" />
</div>
</div>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 566f5c68e66..ec3ee407f0a 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -151,10 +151,10 @@ export default {
<template>
<li
- class="note timeline-entry"
:id="noteAnchorId"
:class="classNameBindings"
- :data-award-url="note.toggle_award_path">
+ :data-award-url="note.toggle_award_path"
+ class="note timeline-entry">
<div class="timeline-entry-inner">
<div class="timeline-icon">
<user-avatar-link
@@ -191,12 +191,12 @@ export default {
/>
</div>
<note-body
+ ref="noteBody"
:note="note"
:can-edit="note.current_user.can_edit"
:is-editing="isEditing"
@handleFormUpdate="formUpdateHandler"
@cancelFormEdition="formCancelHandler"
- ref="noteBody"
/>
</div>
</div>
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 ba1d8e4d8db..bc84666779e 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
@@ -40,8 +40,8 @@
<gl-modal
id="stop-jobs-modal"
:header-title-text="s__('AdminArea|Stop all jobs?')"
- footer-primary-button-variant="danger"
:footer-primary-button-text="s__('AdminArea|Stop jobs')"
+ footer-primary-button-variant="danger"
@submit="onSubmit"
>
{{ text }}
diff --git a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
index 343c65edb37..ff66d3a8ac4 100644
--- a/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
+++ b/app/assets/javascripts/pages/admin/projects/index/components/delete_project_modal.vue
@@ -83,9 +83,9 @@
id="delete-project-modal"
:title="title"
:text="text"
- kind="danger"
:primary-button-label="primaryButtonLabel"
:submit-disabled="!canSubmit"
+ kind="danger"
@submit="onSubmit"
@cancel="onCancel"
>
@@ -107,15 +107,15 @@
value="delete"
/>
<input
+ :value="csrfToken"
type="hidden"
name="authenticity_token"
- :value="csrfToken"
/>
<input
+ v-model="enteredProjectName"
name="projectName"
class="form-control"
type="text"
- v-model="enteredProjectName"
aria-labelledby="input-label"
autocomplete="off"
/>
diff --git a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
index 9ce176744ba..cc2805a1901 100644
--- a/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
+++ b/app/assets/javascripts/pages/admin/users/components/delete_user_modal.vue
@@ -116,10 +116,10 @@
id="delete-user-modal"
:title="title"
:text="text"
- kind="danger"
:primary-button-label="primaryButtonLabel"
:secondary-button-label="secondaryButtonLabel"
:submit-disabled="!canSubmit"
+ kind="danger"
@submit="onSubmit"
@cancel="onCancel"
>
@@ -141,15 +141,15 @@
value="delete"
/>
<input
+ :value="csrfToken"
type="hidden"
name="authenticity_token"
- :value="csrfToken"
/>
<input
+ v-model="enteredUsername"
type="text"
name="username"
class="form-control"
- v-model="enteredUsername"
aria-labelledby="input-label"
autocomplete="off"
/>
@@ -160,11 +160,11 @@
slot-scope="props"
>
<button
+ :disabled="!canSubmit"
type="button"
class="btn js-secondary-button btn-warning"
- :disabled="!canSubmit"
- @click="onSecondaryAction"
data-dismiss="modal"
+ @click="onSecondaryAction"
>
{{ secondaryButtonLabel }}
</button>
diff --git a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
index 16f792d635a..4061c11ba8f 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/delete_milestone_modal.vue
@@ -96,8 +96,8 @@ Once deleted, it cannot be undone or recovered.`),
id="delete-milestone-modal"
:title="title"
:text="text"
- kind="danger"
:primary-button-label="s__('Milestones|Delete milestone')"
+ kind="danger"
@submit="onSubmit">
<template
diff --git a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
index 2bda2aeb3a1..2c683a39f42 100644
--- a/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
+++ b/app/assets/javascripts/pages/milestones/shared/components/promote_milestone_modal.vue
@@ -53,8 +53,8 @@
<template>
<gl-modal
id="promote-milestone-modal"
- footer-primary-button-variant="warning"
:footer-primary-button-text="s__('Milestones|Promote Milestone')"
+ footer-primary-button-variant="warning"
@submit="onSubmit"
>
<template
diff --git a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
index ad6df51bb7a..5d2247f6c6d 100644
--- a/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
+++ b/app/assets/javascripts/pages/projects/labels/components/promote_label_modal.vue
@@ -71,8 +71,8 @@
<template>
<gl-modal
id="promote-label-modal"
- footer-primary-button-variant="warning"
:footer-primary-button-text="s__('Labels|Promote Label')"
+ footer-primary-button-variant="warning"
@submit="onSubmit"
>
<div
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
index 2d18fa2044b..d0613804067 100644
--- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
+++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue
@@ -65,11 +65,11 @@
<div class="cron-preset-radio-input">
<input
id="custom"
- class="label-light"
- type="radio"
:name="inputNameAttribute"
:value="cronInterval"
:checked="isEditable"
+ class="label-light"
+ type="radio"
@click="toggleCustomInput(true)"
/>
@@ -90,11 +90,11 @@
<div class="cron-preset-radio-input">
<input
id="every-day"
- class="label-light"
- type="radio"
v-model="cronInterval"
:name="inputNameAttribute"
:value="cronIntervalPresets.everyDay"
+ class="label-light"
+ type="radio"
@click="toggleCustomInput(false)"
/>
@@ -109,11 +109,11 @@
<div class="cron-preset-radio-input">
<input
id="every-week"
- class="label-light"
- type="radio"
v-model="cronInterval"
:name="inputNameAttribute"
:value="cronIntervalPresets.everyWeek"
+ class="label-light"
+ type="radio"
@click="toggleCustomInput(false)"
/>
@@ -128,11 +128,11 @@
<div class="cron-preset-radio-input">
<input
id="every-month"
- class="label-light"
- type="radio"
v-model="cronInterval"
:name="inputNameAttribute"
:value="cronIntervalPresets.everyMonth"
+ class="label-light"
+ type="radio"
@click="toggleCustomInput(false)"
/>
@@ -147,13 +147,13 @@
<div class="cron-interval-input-wrapper">
<input
id="schedule_cron"
- class="form-control inline cron-interval-input"
- type="text"
:placeholder="__('Define a custom pattern with cron syntax')"
- required="true"
v-model="cronInterval"
:name="inputNameAttribute"
:disabled="!isEditable"
+ class="form-control inline cron-interval-input"
+ type="text"
+ required="true"
/>
</div>
</div>
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 6c2a785c0af..37ef77c8e43 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
@@ -22,4 +22,18 @@ document.addEventListener('DOMContentLoaded', () => {
errorBox: variableListEl.querySelector('.js-ci-variable-error-box'),
saveEndpoint: variableListEl.dataset.saveEndpoint,
});
+
+ // hide extra auto devops settings based on data-attributes
+ const autoDevOpsSettings = document.querySelector('.js-auto-devops-settings');
+ const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');
+
+ autoDevOpsSettings.addEventListener('click', event => {
+ const target = event.target;
+ if (target.classList.contains('js-toggle-extra-settings')) {
+ autoDevOpsExtraSettings.classList.toggle(
+ 'hidden',
+ !!(target.dataset && target.dataset.hideExtraSettings),
+ );
+ }
+ });
});
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
index 9b13b2a524f..06101290f6c 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/project_feature_setting.vue
@@ -72,25 +72,25 @@
<template>
<div
- class="project-feature-controls"
:data-for="name"
+ class="project-feature-controls"
>
<input
v-if="name"
- type="hidden"
:name="name"
:value="value"
+ type="hidden"
/>
<project-feature-toggle
:value="featureEnabled"
- @change="toggleFeature"
:disabled-input="disabledInput"
+ @change="toggleFeature"
/>
<div class="select-wrapper">
<select
+ :disabled="displaySelectInput"
class="form-control project-repo-select select-control"
@change="selectOption"
- :disabled="displaySelectInput"
>
<option
v-for="[optionValue, optionName] in displayOptions"
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 06b0ab184ed..ae88b765abf 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -175,16 +175,16 @@
<div>
<div class="project-visibility-setting">
<project-setting-row
- label="Project visibility"
:help-path="visibilityHelpPath"
+ label="Project visibility"
>
<div class="project-feature-controls">
<div class="select-wrapper">
<select
- name="project[visibility_level]"
v-model="visibilityLevel"
- class="form-control select-control"
:disabled="!canChangeVisibilityLevel"
+ name="project[visibility_level]"
+ class="form-control select-control"
>
<option
:value="visibilityOptions.PRIVATE"
@@ -219,30 +219,30 @@
class="request-access"
>
<input
+ :value="requestAccessEnabled"
type="hidden"
name="project[request_access_enabled]"
- :value="requestAccessEnabled"
/>
<input
- type="checkbox"
v-model="requestAccessEnabled"
+ type="checkbox"
/>
Allow users to request access
</label>
</project-setting-row>
</div>
<div
- class="project-feature-settings"
:class="{ 'highlight-changes': highlightChangesClass }"
+ class="project-feature-settings"
>
<project-setting-row
label="Issues"
help-text="Lightweight issue tracking system for this project"
>
<project-feature-setting
- name="project[project_feature_attributes][issues_access_level]"
:options="featureAccessLevelOptions"
v-model="issuesAccessLevel"
+ name="project[project_feature_attributes][issues_access_level]"
/>
</project-setting-row>
<project-setting-row
@@ -250,9 +250,9 @@
help-text="View and edit files in this project"
>
<project-feature-setting
- name="project[project_feature_attributes][repository_access_level]"
:options="featureAccessLevelOptions"
v-model="repositoryAccessLevel"
+ name="project[project_feature_attributes][repository_access_level]"
/>
</project-setting-row>
<div class="project-feature-setting-group">
@@ -261,10 +261,10 @@
help-text="Submit changes to be merged upstream"
>
<project-feature-setting
- name="project[project_feature_attributes][merge_requests_access_level]"
:options="repoFeatureAccessLevelOptions"
v-model="mergeRequestsAccessLevel"
:disabled-input="!repositoryEnabled"
+ name="project[project_feature_attributes][merge_requests_access_level]"
/>
</project-setting-row>
<project-setting-row
@@ -272,34 +272,34 @@
help-text="Build, test, and deploy your changes"
>
<project-feature-setting
- name="project[project_feature_attributes][builds_access_level]"
:options="repoFeatureAccessLevelOptions"
v-model="buildsAccessLevel"
:disabled-input="!repositoryEnabled"
+ name="project[project_feature_attributes][builds_access_level]"
/>
</project-setting-row>
<project-setting-row
v-if="registryAvailable"
- label="Container registry"
:help-path="registryHelpPath"
+ label="Container registry"
help-text="Every project can have its own space to store its Docker images"
>
<project-feature-toggle
- name="project[container_registry_enabled]"
v-model="containerRegistryEnabled"
:disabled-input="!repositoryEnabled"
+ name="project[container_registry_enabled]"
/>
</project-setting-row>
<project-setting-row
v-if="lfsAvailable"
- label="Git Large File Storage"
:help-path="lfsHelpPath"
+ label="Git Large File Storage"
help-text="Manages large files such as audio, video, and graphics files"
>
<project-feature-toggle
- name="project[lfs_enabled]"
v-model="lfsEnabled"
:disabled-input="!repositoryEnabled"
+ name="project[lfs_enabled]"
/>
</project-setting-row>
</div>
@@ -308,9 +308,9 @@
help-text="Pages for project documentation"
>
<project-feature-setting
- name="project[project_feature_attributes][wiki_access_level]"
:options="featureAccessLevelOptions"
v-model="wikiAccessLevel"
+ name="project[project_feature_attributes][wiki_access_level]"
/>
</project-setting-row>
<project-setting-row
@@ -318,9 +318,9 @@
help-text="Share code pastes with others out of Git repository"
>
<project-feature-setting
- name="project[project_feature_attributes][snippets_access_level]"
:options="featureAccessLevelOptions"
v-model="snippetsAccessLevel"
+ name="project[project_feature_attributes][snippets_access_level]"
/>
</project-setting-row>
</div>
diff --git a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
index 5765eed4d45..0289209ff1e 100644
--- a/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
+++ b/app/assets/javascripts/pages/projects/wikis/components/delete_wiki_modal.vue
@@ -50,8 +50,8 @@ export default {
<gl-modal
id="delete-wiki-modal"
:header-title-text="title"
- footer-primary-button-variant="danger"
:footer-primary-button-text="s__('WikiPageConfirmDelete|Delete page')"
+ footer-primary-button-variant="danger"
@submit="onSubmit"
>
{{ message }}
@@ -68,9 +68,9 @@ export default {
value="delete"
/>
<input
+ :value="csrfToken"
type="hidden"
name="authenticity_token"
- :value="csrfToken"
/>
</form>
</gl-modal>
diff --git a/app/assets/javascripts/pages/sessions/new/username_validator.js b/app/assets/javascripts/pages/sessions/new/username_validator.js
index 825de01b5a2..87213c94eda 100644
--- a/app/assets/javascripts/pages/sessions/new/username_validator.js
+++ b/app/assets/javascripts/pages/sessions/new/username_validator.js
@@ -62,13 +62,13 @@ export default class UsernameValidator {
return this.setPendingState();
}
- if (!this.state.available) {
- return this.setUnavailableState();
- }
-
if (!this.state.valid) {
return this.setInvalidState();
}
+
+ if (!this.state.available) {
+ return this.setUnavailableState();
+ }
}
interceptInvalid(event) {
@@ -89,7 +89,6 @@ export default class UsernameValidator {
setAvailabilityState(usernameTaken) {
if (usernameTaken) {
- this.state.valid = false;
this.state.available = false;
} else {
this.state.available = true;
diff --git a/app/assets/javascripts/pdf/index.vue b/app/assets/javascripts/pdf/index.vue
index 00f32d9de78..2f480ecdc69 100644
--- a/app/assets/javascripts/pdf/index.vue
+++ b/app/assets/javascripts/pdf/index.vue
@@ -56,8 +56,8 @@
<template>
<div
- class="pdf-viewer"
- v-if="hasPDF">
+ v-if="hasPDF"
+ class="pdf-viewer">
<page
v-for="(page, index) in pages"
:key="index"
diff --git a/app/assets/javascripts/pdf/page/index.vue b/app/assets/javascripts/pdf/page/index.vue
index fcba819beba..9f06833d560 100644
--- a/app/assets/javascripts/pdf/page/index.vue
+++ b/app/assets/javascripts/pdf/page/index.vue
@@ -43,9 +43,9 @@
<template>
<canvas
- class="pdf-page"
ref="canvas"
:data-page="number"
+ class="pdf-page"
>
</canvas>
</template>
diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
index 96189e7033a..d765ff3b41c 100644
--- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue
+++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue
@@ -39,9 +39,9 @@ export default {
</script>
<template>
<div
+ v-if="currentRequest.details"
:id="`peek-view-${metric}`"
class="view"
- v-if="currentRequest.details"
>
<button
:data-target="`#modal-peek-${metric}-details`"
diff --git a/app/assets/javascripts/pipelines/components/graph/action_component.vue b/app/assets/javascripts/pipelines/components/graph/action_component.vue
index 82b4ce083fb..db0505a55fe 100644
--- a/app/assets/javascripts/pipelines/components/graph/action_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/action_component.vue
@@ -83,15 +83,15 @@ export default {
</script>
<template>
<button
- type="button"
- @click="onClickAction"
v-tooltip
:title="tooltipText"
+ :class="cssClass"
+ :disabled="isDisabled"
+ type="button"
class="js-ci-action btn btn-blank
btn-transparent ci-action-icon-container ci-action-icon-wrapper"
- :class="cssClass"
data-container="body"
- :disabled="isDisabled"
+ @click="onClickAction"
>
<icon :name="actionIcon"/>
</button>
diff --git a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
index e64afc94ef9..04120d45834 100644
--- a/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/dropdown_job_component.vue
@@ -82,12 +82,12 @@ export default {
<div class="ci-job-dropdown-container dropdown dropright">
<button
v-tooltip
+ :title="tooltipText"
type="button"
data-toggle="dropdown"
data-container="body"
data-boundary="viewport"
class="dropdown-menu-toggle build-content"
- :title="tooltipText"
>
<job-name-component
diff --git a/app/assets/javascripts/pipelines/components/graph/job_component.vue b/app/assets/javascripts/pipelines/components/graph/job_component.vue
index dc16d395bcb..886e62ab1a7 100644
--- a/app/assets/javascripts/pipelines/components/graph/job_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/job_component.vue
@@ -107,11 +107,11 @@ export default {
</a>
<div
- v-else
v-tooltip
- class="js-job-component-tooltip non-details-job-component"
+ v-else
:title="tooltipText"
:class="cssClassJobName"
+ class="js-job-component-tooltip non-details-job-component"
data-html="true"
data-container="body"
>
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 f32368947e8..2c728582b7c 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -52,8 +52,8 @@ export default {
</script>
<template>
<li
- class="stage-column"
- :class="stageConnectorClass">
+ :class="stageConnectorClass"
+ class="stage-column">
<div class="stage-name">
{{ title }}
</div>
@@ -62,9 +62,9 @@ export default {
<li
v-for="(job, index) in jobs"
:key="job.id"
- class="build"
:class="buildConnnectorClass(index)"
:id="jobId(job)"
+ class="build"
>
<div class="curve"></div>
diff --git a/app/assets/javascripts/pipelines/components/header_component.vue b/app/assets/javascripts/pipelines/components/header_component.vue
index e08c2092680..5b212ee8931 100644
--- a/app/assets/javascripts/pipelines/components/header_component.vue
+++ b/app/assets/javascripts/pipelines/components/header_component.vue
@@ -82,11 +82,11 @@
<ci-header
v-if="shouldRenderContent"
:status="status"
- item-name="Pipeline"
:item-id="pipeline.id"
:time="pipeline.created_at"
:user="pipeline.user"
:actions="actions"
+ item-name="Pipeline"
@actionClicked="postAction"
/>
<loading-icon
diff --git a/app/assets/javascripts/pipelines/components/nav_controls.vue b/app/assets/javascripts/pipelines/components/nav_controls.vue
index eba5678e3e5..1fce9f16ee0 100644
--- a/app/assets/javascripts/pipelines/components/nav_controls.vue
+++ b/app/assets/javascripts/pipelines/components/nav_controls.vue
@@ -50,10 +50,10 @@
<loading-button
v-if="resetCachePath"
- @click="onClickResetCache"
:loading="isResetCacheButtonLoading"
- class="btn btn-default js-clear-cache"
:label="s__('Pipelines|Clear Runner Caches')"
+ class="btn btn-default js-clear-cache"
+ @click="onClickResetCache"
/>
<a
diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue
index 4d965733f95..a107e579457 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue
@@ -55,10 +55,10 @@
<span>by</span>
<user-avatar-link
v-if="user"
- class="js-pipeline-url-user"
:link-href="pipeline.user.path"
:img-src="pipeline.user.avatar_url"
:tooltip-text="pipeline.user.name"
+ class="js-pipeline-url-user"
/>
<span
v-if="!user"
@@ -67,31 +67,31 @@
</span>
<div class="label-container">
<span
- v-if="pipeline.flags.latest"
v-tooltip
+ v-if="pipeline.flags.latest"
class="js-pipeline-url-latest badge badge-success"
title="Latest pipeline for this branch">
latest
</span>
<span
- v-if="pipeline.flags.yaml_errors"
v-tooltip
- class="js-pipeline-url-yaml badge badge-danger"
- :title="pipeline.yaml_errors">
+ v-if="pipeline.flags.yaml_errors"
+ :title="pipeline.yaml_errors"
+ class="js-pipeline-url-yaml badge badge-danger">
yaml invalid
</span>
<span
- v-if="pipeline.flags.failure_reason"
v-tooltip
- class="js-pipeline-url-failure badge badge-danger"
- :title="pipeline.failure_reason">
+ v-if="pipeline.flags.failure_reason"
+ :title="pipeline.failure_reason"
+ class="js-pipeline-url-failure badge badge-danger">
error
</span>
<a
+ v-popover="popoverOptions"
v-if="pipeline.flags.auto_devops"
tabindex="0"
class="js-pipeline-url-autodevops badge badge-info autodevops-badge"
- v-popover="popoverOptions"
role="button">
Auto DevOps
</a>
diff --git a/app/assets/javascripts/pipelines/components/pipelines.vue b/app/assets/javascripts/pipelines/components/pipelines.vue
index 497a09cec65..b31b4bad7a0 100644
--- a/app/assets/javascripts/pipelines/components/pipelines.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines.vue
@@ -282,8 +282,8 @@
<template>
<div class="pipelines-container">
<div
- class="top-area scrolling-tabs-container inner-page-scroll-tabs"
v-if="shouldRenderTabs || shouldRenderButtons"
+ class="top-area scrolling-tabs-container inner-page-scroll-tabs"
>
<div class="fade-left">
<i
@@ -303,8 +303,8 @@
<navigation-tabs
v-if="shouldRenderTabs"
:tabs="tabs"
- @onChangeTab="onChangeTab"
scope="pipelines"
+ @onChangeTab="onChangeTab"
/>
<navigation-controls
@@ -312,8 +312,8 @@
:new-pipeline-path="newPipelinePath"
:reset-cache-path="resetCachePath"
:ci-lint-path="ciLintPath"
- @resetRunnersCache="handleResetRunnersCache"
:is-reset-cache-button-loading="isResetCacheButtonLoading"
+ @resetRunnersCache="handleResetRunnersCache"
/>
</div>
@@ -347,8 +347,8 @@
/>
<div
- class="table-holder"
v-else-if="stateToRender === $options.stateMap.tableList"
+ class="table-holder"
>
<pipelines-table-component
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index e9bc3cf14ca..5070c253f11 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -44,13 +44,13 @@
<div class="btn-group">
<button
v-tooltip
+ :disabled="isLoading"
type="button"
class="dropdown-new btn btn-default js-pipeline-dropdown-manual-actions"
title="Manual job"
data-toggle="dropdown"
data-placement="top"
aria-label="Manual job"
- :disabled="isLoading"
>
<icon
name="play"
@@ -69,11 +69,11 @@
:key="i"
>
<button
+ :class="{ disabled: isActionDisabled(action) }"
+ :disabled="isActionDisabled(action)"
type="button"
class="js-pipeline-action-link no-btn btn"
@click="onClickAction(action.path)"
- :class="{ disabled: isActionDisabled(action) }"
- :disabled="isActionDisabled(action)"
>
{{ action.name }}
</button>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
index 31fcc9dd412..490df47e154 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_artifacts.vue
@@ -42,9 +42,9 @@
v-for="(artifact, i) in artifacts"
:key="i">
<a
+ :href="artifact.path"
rel="nofollow"
download
- :href="artifact.path"
>
Download {{ artifact.name }} artifacts
</a>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 4318abe97e0..2e777783636 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -114,8 +114,8 @@
<modal
id="confirmation-modal"
:header-title-text="modalTitle"
- footer-primary-button-variant="danger"
:footer-primary-button-text="s__('Pipeline|Stop pipeline')"
+ footer-primary-button-variant="danger"
@submit="onSubmit"
>
<span v-html="modalText"></span>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index a3c17479e6f..b2744a30c2a 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -301,9 +301,9 @@
<div class="table-mobile-content">
<template v-if="pipeline.details.stages.length > 0">
<div
- class="stage-container dropdown js-mini-pipeline-graph"
v-for="(stage, index) in pipeline.details.stages"
- :key="index">
+ :key="index"
+ class="stage-container dropdown js-mini-pipeline-graph">
<pipeline-stage
:type="$options.pipelinesTable"
:stage="stage"
@@ -331,28 +331,28 @@
<pipelines-artifacts-component
v-if="pipeline.details.artifacts.length"
- class="d-none d-sm-none d-md-block"
:artifacts="pipeline.details.artifacts"
+ class="d-none d-sm-none d-md-block"
/>
<loading-button
v-if="pipeline.flags.retryable"
- @click="handleRetryClick"
- container-class="js-pipelines-retry-button btn btn-default btn-retry"
:loading="isRetrying"
:disabled="isRetrying"
+ container-class="js-pipelines-retry-button btn btn-default btn-retry"
+ @click="handleRetryClick"
>
<icon name="repeat" />
</loading-button>
<loading-button
v-if="pipeline.flags.cancelable"
- @click="handleCancelClick"
+ :loading="isCancelling"
+ :disabled="isCancelling"
data-toggle="modal"
data-target="#confirmation-modal"
container-class="js-pipelines-cancel-button btn btn-remove"
- :loading="isCancelling"
- :disabled="isCancelling"
+ @click="handleCancelClick"
>
<icon name="close" />
</loading-button>
diff --git a/app/assets/javascripts/pipelines/components/stage.vue b/app/assets/javascripts/pipelines/components/stage.vue
index f9769815796..4dd2fb9fbed 100644
--- a/app/assets/javascripts/pipelines/components/stage.vue
+++ b/app/assets/javascripts/pipelines/components/stage.vue
@@ -158,22 +158,22 @@ export default {
<div class="dropdown">
<button
v-tooltip
+ id="stageDropdown"
+ ref="dropdown"
:class="triggerButtonClass"
- @click="onClickStage"
- class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
:title="stage.title"
+ class="mini-pipeline-graph-dropdown-toggle js-builds-dropdown-button"
data-placement="top"
data-toggle="dropdown"
type="button"
- id="stageDropdown"
aria-haspopup="true"
aria-expanded="false"
- ref="dropdown"
+ @click="onClickStage"
>
<span
- aria-hidden="true"
:aria-label="stage.title"
+ aria-hidden="true"
>
<icon :name="borderlessIcon" />
</span>
diff --git a/app/assets/javascripts/pipelines/components/time_ago.vue b/app/assets/javascripts/pipelines/components/time_ago.vue
index 79dbdca4010..0a97df2dc18 100644
--- a/app/assets/javascripts/pipelines/components/time_ago.vue
+++ b/app/assets/javascripts/pipelines/components/time_ago.vue
@@ -66,8 +66,8 @@
</div>
<div class="table-mobile-content">
<p
- class="duration"
v-if="hasDuration"
+ class="duration"
>
<span v-html="iconTimerSvg">
</span>
@@ -75,8 +75,8 @@
</p>
<p
- class="finished-at d-none d-sm-none d-md-block"
v-if="hasFinishedTime"
+ class="finished-at d-none d-sm-none d-md-block"
>
<i
@@ -87,9 +87,9 @@
<time
v-tooltip
+ :title="tooltipTitle(finishedTime)"
data-placement="top"
- data-container="body"
- :title="tooltipTitle(finishedTime)">
+ data-container="body">
{{ timeFormated(finishedTime) }}
</time>
</p>
diff --git a/app/assets/javascripts/profile/account/components/delete_account_modal.vue b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
index f50002afbf2..974629fa2af 100644
--- a/app/assets/javascripts/profile/account/components/delete_account_modal.vue
+++ b/app/assets/javascripts/profile/account/components/delete_account_modal.vue
@@ -80,10 +80,10 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
id="delete-account-modal"
:title="s__('Profiles|Delete your account?')"
:text="text"
- kind="danger"
:primary-button-label="s__('Profiles|Delete account')"
- @submit="onSubmit"
- :submit-disabled="!canSubmit()">
+ :submit-disabled="!canSubmit()"
+ kind="danger"
+ @submit="onSubmit">
<template
slot="body"
@@ -101,9 +101,9 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
value="delete"
/>
<input
+ :value="csrfToken"
type="hidden"
name="authenticity_token"
- :value="csrfToken"
/>
<p
@@ -114,18 +114,18 @@ Once you confirm %{deleteAccount}, it cannot be undone or recovered.`),
<input
v-if="confirmWithPassword"
+ v-model="enteredPassword"
name="password"
class="form-control"
type="password"
- v-model="enteredPassword"
aria-labelledby="input-label"
/>
<input
v-else
+ v-model="enteredUsername"
name="username"
class="form-control"
type="text"
- v-model="enteredUsername"
aria-labelledby="input-label"
/>
</form>
diff --git a/app/assets/javascripts/profile/account/components/update_username.vue b/app/assets/javascripts/profile/account/components/update_username.vue
index b37febe523c..ef484ddfd61 100644
--- a/app/assets/javascripts/profile/account/components/update_username.vue
+++ b/app/assets/javascripts/profile/account/components/update_username.vue
@@ -93,10 +93,10 @@ Please update your Git repository remotes as soon as possible.`),
</div>
<input
:id="$options.inputId"
- class="form-control"
- required="required"
v-model="newUsername"
:disabled="isRequestPending"
+ class="form-control"
+ required="required"
/>
</div>
<p class="form-text text-muted">
@@ -105,18 +105,18 @@ Please update your Git repository remotes as soon as possible.`),
</div>
<button
:data-target="`#${$options.modalId}`"
+ :disabled="isRequestPending || newUsername === username"
class="btn btn-warning"
type="button"
data-toggle="modal"
- :disabled="isRequestPending || newUsername === username"
>
{{ $options.buttonText }}
</button>
<gl-modal
:id="$options.modalId"
:header-title-text="s__('Profiles|Change username') + '?'"
- footer-primary-button-variant="warning"
:footer-primary-button-text="$options.buttonText"
+ footer-primary-button-variant="warning"
@submit="onConfirm"
>
<span v-html="modalText"></span>
diff --git a/app/assets/javascripts/project_label_subscription.js b/app/assets/javascripts/project_label_subscription.js
index 6f06944ebb6..9049f87e037 100644
--- a/app/assets/javascripts/project_label_subscription.js
+++ b/app/assets/javascripts/project_label_subscription.js
@@ -3,6 +3,17 @@ import { __ } from './locale';
import axios from './lib/utils/axios_utils';
import flash from './flash';
+const tooltipTitles = {
+ group: {
+ subscribed: __('Unsubscribe at group level'),
+ unsubscribed: __('Subscribe at group level'),
+ },
+ project: {
+ subscribed: __('Unsubscribe at project level'),
+ unsubscribed: __('Subscribe at project level'),
+ },
+};
+
export default class ProjectLabelSubscription {
constructor(container) {
this.$container = $(container);
@@ -15,12 +26,10 @@ export default class ProjectLabelSubscription {
event.preventDefault();
const $btn = $(event.currentTarget);
- const $span = $btn.find('span');
const url = $btn.attr('data-url');
const oldStatus = $btn.attr('data-status');
$btn.addClass('disabled');
- $span.toggleClass('hidden');
axios.post(url).then(() => {
let newStatus;
@@ -32,21 +41,28 @@ export default class ProjectLabelSubscription {
[newStatus, newAction] = ['unsubscribed', 'Subscribe'];
}
- $span.toggleClass('hidden');
$btn.removeClass('disabled');
this.$buttons.attr('data-status', newStatus);
this.$buttons.find('> span').text(newAction);
- this.$buttons.map((button) => {
+ this.$buttons.map((i, button) => {
const $button = $(button);
+ const originalTitle = $button.attr('data-original-title');
- if ($button.attr('data-original-title')) {
- $button.tooltip('hide').attr('data-original-title', newAction).tooltip('_fixTitle');
+ if (originalTitle) {
+ ProjectLabelSubscription.setNewTitle($button, originalTitle, newStatus, newAction);
}
return button;
});
}).catch(() => flash(__('There was an error subscribing to this label.')));
}
+
+ static setNewTitle($button, originalTitle, newStatus) {
+ const type = /group/.test(originalTitle) ? 'group' : 'project';
+ const newTitle = tooltipTitles[type][newStatus];
+
+ $button.attr('title', newTitle).tooltip('_fixTitle');
+ }
}
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
index 6ed35c0a981..d4497924ad8 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_machine_type_dropdown.vue
@@ -131,12 +131,12 @@ export default {
</div>
</div>
<span
- class="form-text"
+ v-if="hasErrors"
:class="{
'text-danger': hasErrors,
'text-muted': !hasErrors
}"
- v-if="hasErrors"
+ class="form-text"
>
{{ errorMessage }}
</span>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
index 542d4d21a22..08d0a122579 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_project_id_dropdown.vue
@@ -192,11 +192,11 @@ export default {
</div>
</div>
<span
- class="form-text"
:class="{
'text-danger': hasErrors,
'text-muted': !hasErrors
}"
+ class="form-text"
v-html="helpText"
></span>
</div>
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
index bc28f8b5df4..b5476684c6a 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
@@ -105,12 +105,12 @@ export default {
</div>
</div>
<span
- class="form-text"
+ v-if="hasErrors"
:class="{
'text-danger': hasErrors,
'text-muted': !hasErrors
}"
- v-if="hasErrors"
+ class="form-text"
>
{{ errorMessage }}
</span>
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index 63f20a0041d..c772fca14bb 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -100,9 +100,9 @@
<template>
<div>
<loading-icon
+ v-if="isLoading"
label="Loading pipeline status"
size="3"
- v-if="isLoading"
/>
<a
v-else
@@ -112,8 +112,8 @@
v-tooltip
:title="statusTitle"
:aria-label="statusTitle"
- data-container="body"
:status="ciStatus"
+ data-container="body"
/>
</a>
</div>
diff --git a/app/assets/javascripts/projects_dropdown/components/app.vue b/app/assets/javascripts/projects_dropdown/components/app.vue
index 0bbd8a41753..73d49488299 100644
--- a/app/assets/javascripts/projects_dropdown/components/app.vue
+++ b/app/assets/javascripts/projects_dropdown/components/app.vue
@@ -132,14 +132,14 @@ export default {
<div>
<search/>
<loading-icon
- class="loading-animation prepend-top-20"
- size="2"
v-if="isLoadingProjects"
:label="s__('ProjectsDropdown|Loading projects')"
+ class="loading-animation prepend-top-20"
+ size="2"
/>
<div
- class="section-header"
v-if="isFrequentsListVisible"
+ class="section-header"
>
{{ s__('ProjectsDropdown|Frequently visited') }}
</div>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
index 246dbeaaded..625e0aa548c 100644
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
+++ b/app/assets/javascripts/projects_dropdown/components/projects_list_frequent.vue
@@ -37,14 +37,14 @@
class="list-unstyled"
>
<li
- class="section-empty"
v-if="isListEmpty"
+ class="section-empty"
>
{{ listEmptyMessage }}
</li>
<projects-list-item
- v-else
v-for="(project, index) in projects"
+ v-else
:key="index"
:project-id="project.id"
:project-name="project.name"
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
index 759cdd1ded9..eafbf6c99e2 100644
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
+++ b/app/assets/javascripts/projects_dropdown/components/projects_list_item.vue
@@ -79,36 +79,36 @@
class="projects-list-item-container"
>
<a
- class="clearfix"
:href="webUrl"
+ class="clearfix"
>
<div
class="project-item-avatar-container"
>
<img
v-if="hasAvatar"
- class="avatar s32"
:src="avatarUrl"
+ class="avatar s32"
/>
<identicon
v-else
- size-class="s32"
:entity-id="projectId"
:entity-name="projectName"
+ size-class="s32"
/>
</div>
<div
class="project-item-metadata-container"
>
<div
- class="project-title"
:title="projectName"
+ class="project-title"
v-html="highlightedProjectName"
>
</div>
<div
- class="project-namespace"
:title="namespace"
+ class="project-namespace"
>{{ truncatedNamespace }}</div>
</div>
</a>
diff --git a/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue b/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
index 8d0c29177e6..76e9cb9e53f 100644
--- a/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
+++ b/app/assets/javascripts/projects_dropdown/components/projects_list_search.vue
@@ -48,8 +48,8 @@ export default {
{{ listEmptyMessage }}
</li>
<projects-list-item
- v-else
v-for="(project, index) in projects"
+ v-else
:key="index"
:project-id="project.id"
:project-name="project.name"
diff --git a/app/assets/javascripts/projects_dropdown/components/search.vue b/app/assets/javascripts/projects_dropdown/components/search.vue
index 7fcd62dcdb8..28f2a18f2a6 100644
--- a/app/assets/javascripts/projects_dropdown/components/search.vue
+++ b/app/assets/javascripts/projects_dropdown/components/search.vue
@@ -49,11 +49,11 @@
class="search-input-container d-none d-sm-block"
>
<input
- type="search"
- class="form-control"
ref="search"
v-model="searchQuery"
:placeholder="s__('ProjectsDropdown|Search your projects')"
+ type="search"
+ class="form-control"
/>
<i
v-if="!searchQuery"
diff --git a/app/assets/javascripts/registry/components/app.vue b/app/assets/javascripts/registry/components/app.vue
index ea0f7199a70..31f88675912 100644
--- a/app/assets/javascripts/registry/components/app.vue
+++ b/app/assets/javascripts/registry/components/app.vue
@@ -48,8 +48,8 @@
/>
<collapsible-container
- v-else-if="!isLoading && repos.length"
v-for="(item, index) in repos"
+ v-else-if="!isLoading && repos.length"
:key="index"
:repo="item"
/>
diff --git a/app/assets/javascripts/registry/components/collapsible_container.vue b/app/assets/javascripts/registry/components/collapsible_container.vue
index 2fc3778820b..4116c4a0489 100644
--- a/app/assets/javascripts/registry/components/collapsible_container.vue
+++ b/app/assets/javascripts/registry/components/collapsible_container.vue
@@ -62,15 +62,15 @@
<div class="container-image-head">
<button
type="button"
- @click="toggleRepo"
class="js-toggle-repo btn-link"
+ @click="toggleRepo"
>
<i
- class="fa"
:class="{
'fa-chevron-right': !isOpen,
'fa-chevron-up': isOpen,
}"
+ class="fa"
aria-hidden="true"
>
</i>
@@ -86,12 +86,12 @@
<div class="controls d-none d-sm-block float-right">
<button
+ v-tooltip
v-if="repo.canDelete"
- type="button"
- class="js-remove-repo btn btn-danger"
:title="s__('ContainerRegistry|Remove repository')"
:aria-label="s__('ContainerRegistry|Remove repository')"
- v-tooltip
+ type="button"
+ class="js-remove-repo btn btn-danger"
@click="handleDeleteRepository"
>
<i
diff --git a/app/assets/javascripts/registry/components/table_registry.vue b/app/assets/javascripts/registry/components/table_registry.vue
index e4a4b3bb129..9f4973c3490 100644
--- a/app/assets/javascripts/registry/components/table_registry.vue
+++ b/app/assets/javascripts/registry/components/table_registry.vue
@@ -118,13 +118,13 @@
<td class="content">
<button
+ v-tooltip
v-if="item.canDelete"
- type="button"
- class="js-delete-registry btn btn-danger d-none d-sm-block float-right"
:title="s__('ContainerRegistry|Remove tag')"
:aria-label="s__('ContainerRegistry|Remove tag')"
+ type="button"
+ class="js-delete-registry btn btn-danger d-none d-sm-block float-right"
data-container="body"
- v-tooltip
@click="handleDeleteRegistry(item)"
>
<i
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.vue b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
index 5a374d84796..d22a1e1ac66 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.vue
@@ -125,13 +125,13 @@ export default {
<template>
<div>
<div
- class="sidebar-collapsed-icon sidebar-collapsed-user"
- :class="{ 'multiple-users': hasMoreThanOneAssignee }"
v-tooltip
+ :class="{ 'multiple-users': hasMoreThanOneAssignee }"
+ :title="collapsedTooltipTitle"
+ class="sidebar-collapsed-icon sidebar-collapsed-user"
data-container="body"
data-placement="left"
data-boundary="viewport"
- :title="collapsedTooltipTitle"
>
<i
v-if="hasNoUsers"
@@ -140,17 +140,17 @@ export default {
>
</i>
<button
- type="button"
- class="btn-link"
v-for="(user, index) in users"
v-if="shouldRenderCollapsedAssignee(index)"
:key="user.id"
+ type="button"
+ class="btn-link"
>
<img
- width="24"
- class="avatar avatar-inline s24"
:alt="assigneeAlt(user)"
:src="avatarUrl(user)"
+ width="24"
+ class="avatar avatar-inline s24"
/>
<span class="author">
{{ user.name }}
@@ -186,14 +186,14 @@ export default {
</template>
<template v-else-if="hasOneUser">
<a
- class="author_link bold"
:href="assigneeUrl(firstUser)"
+ class="author_link bold"
>
<img
- width="32"
- class="avatar avatar-inline s32"
:alt="assigneeAlt(firstUser)"
:src="avatarUrl(firstUser)"
+ width="32"
+ class="avatar avatar-inline s32"
/>
<span class="author">
{{ firstUser.name }}
@@ -206,23 +206,23 @@ export default {
<template v-else>
<div class="user-list">
<div
- class="user-item"
v-for="(user, index) in users"
v-if="renderAssignee(index)"
:key="user.id"
+ class="user-item"
>
<a
+ :href="assigneeUrl(user)"
+ :data-title="user.name"
class="user-link has-tooltip"
data-container="body"
data-placement="bottom"
- :href="assigneeUrl(user)"
- :data-title="user.name"
>
<img
- width="32"
- class="avatar avatar-inline s32"
:alt="assigneeAlt(user)"
:src="avatarUrl(user)"
+ width="32"
+ class="avatar avatar-inline s32"
/>
</a>
</div>
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
index b04a2eff798..123c92aff64 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees.vue
@@ -90,12 +90,12 @@ export default {
/>
<assignees
v-if="!store.isFetching.assignees"
- class="value"
:root-path="store.rootPath"
:users="store.assignees"
:editable="store.editable"
- @assign-self="assignSelf"
:issuable-type="issuableType"
+ class="value"
+ @assign-self="assignSelf"
/>
</div>
</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
index 3f6e2f05396..1b866cca4b1 100644
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
@@ -70,13 +70,13 @@ export default {
<template>
<div class="block issuable-sidebar-item confidentiality">
<div
- class="sidebar-collapsed-icon"
- @click="toggleForm"
v-tooltip
+ :title="tooltipLabel"
+ class="sidebar-collapsed-icon"
data-container="body"
data-placement="left"
data-boundary="viewport"
- :title="tooltipLabel"
+ @click="toggleForm"
>
<icon
:name="confidentialityIcon"
@@ -104,8 +104,8 @@ export default {
v-if="!isConfidential"
class="no-value sidebar-item-value">
<icon
- name="eye"
:size="16"
+ name="eye"
aria-hidden="true"
class="sidebar-item-icon inline"
/>
@@ -115,8 +115,8 @@ export default {
v-else
class="value sidebar-item-value hide-collapsed">
<icon
- name="eye-slash"
:size="16"
+ name="eye-slash"
aria-hidden="true"
class="sidebar-item-icon inline is-active"
/>
diff --git a/app/assets/javascripts/sidebar/components/lock/edit_form.vue b/app/assets/javascripts/sidebar/components/lock/edit_form.vue
index d392977e5e2..4906dad22e1 100644
--- a/app/assets/javascripts/sidebar/components/lock/edit_form.vue
+++ b/app/assets/javascripts/sidebar/components/lock/edit_form.vue
@@ -44,14 +44,14 @@ export default {
<div class="dropdown show">
<div class="dropdown-menu sidebar-item-warning-message">
<p
- class="text"
v-if="isLocked"
+ class="text"
v-html="unlockWarning">
</p>
<p
- class="text"
v-else
+ class="text"
v-html="lockWarning">
</p>
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 fb69c741dcd..10bedf8af1f 100644
--- a/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
+++ b/app/assets/javascripts/sidebar/components/lock/lock_issue_sidebar.vue
@@ -94,13 +94,13 @@ export default {
<template>
<div class="block issuable-sidebar-item lock">
<div
- class="sidebar-collapsed-icon"
- @click="toggleForm"
v-tooltip
+ :title="tooltipLabel"
+ class="sidebar-collapsed-icon"
data-container="body"
data-placement="left"
data-boundary="viewport"
- :title="tooltipLabel"
+ @click="toggleForm"
>
<icon
:name="lockIcon"
@@ -134,8 +134,8 @@ export default {
class="value sidebar-item-value"
>
<icon
- name="lock"
:size="16"
+ name="lock"
aria-hidden="true"
class="sidebar-item-icon inline is-active"
/>
@@ -147,8 +147,8 @@ export default {
class="no-value sidebar-item-value hide-collapsed"
>
<icon
- name="lock-open"
:size="16"
+ name="lock-open"
aria-hidden="true"
class="sidebar-item-icon inline"
/>
diff --git a/app/assets/javascripts/sidebar/components/participants/participants.vue b/app/assets/javascripts/sidebar/components/participants/participants.vue
index 0a945fc7fd5..33dd6c981b6 100644
--- a/app/assets/javascripts/sidebar/components/participants/participants.vue
+++ b/app/assets/javascripts/sidebar/components/participants/participants.vue
@@ -80,12 +80,12 @@
<template>
<div>
<div
- class="sidebar-collapsed-icon"
v-tooltip
+ :title="participantLabel"
+ class="sidebar-collapsed-icon"
data-container="body"
data-placement="left"
data-boundary="viewport"
- :title="participantLabel"
@click="onClickCollapsedIcon"
>
<i
@@ -119,15 +119,15 @@
class="participants-author js-participants-author"
>
<a
- class="author_link"
:href="participant.web_url"
+ class="author_link"
>
<user-avatar-image
:lazy="true"
:img-src="participant.avatar_url"
- css-classes="avatar-inline"
:size="24"
:tooltip-text="participant.name"
+ css-classes="avatar-inline"
tooltip-placement="bottom"
/>
</a>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index 6745c1aafff..448c8fc3602 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -97,9 +97,9 @@
</span>
<toggle-button
ref="toggleButton"
- class="float-right hide-collapsed js-issuable-subscribe-button"
:is-loading="showLoadingState"
:value="subscribed"
+ class="float-right hide-collapsed js-issuable-subscribe-button"
@change="toggleSubscription"
/>
</div>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
index 209af1ce152..1d030c4f67f 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/collapsed_state.vue
@@ -110,12 +110,12 @@
<template>
<div
- class="sidebar-collapsed-icon"
v-tooltip
+ :title="tooltipText"
+ class="sidebar-collapsed-icon"
data-container="body"
data-placement="left"
data-boundary="viewport"
- :title="tooltipText"
>
<icon name="timer" />
<div class="time-tracking-collapsed-summary">
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
index 6f79310b1cc..d335c3f55af 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/comparison_pane.vue
@@ -1,8 +1,12 @@
<script>
import { parseSeconds, stringifyTime } from '../../../lib/utils/pretty_time';
+import tooltip from '../../../vue_shared/directives/tooltip';
export default {
name: 'TimeTrackingComparisonPane',
+ directives: {
+ tooltip,
+ },
props: {
timeSpent: {
type: Number,
@@ -50,19 +54,17 @@ export default {
<template>
<div class="time-tracking-comparison-pane">
<div
+ v-tooltip
+ :title="timeRemainingTooltip"
+ :class="timeRemainingStatusClass"
class="compare-meter"
data-toggle="tooltip"
data-placement="top"
role="timeRemainingDisplay"
- :aria-valuenow="timeRemainingTooltip"
- :title="timeRemainingTooltip"
- :data-original-title="timeRemainingTooltip"
- :class="timeRemainingStatusClass"
>
<div
- class="meter-container"
- role="timeSpentPercent"
:aria-valuenow="timeRemainingPercent"
+ class="meter-container"
>
<div
:style="{ width: timeRemainingPercent }"
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
index 825063d9ba6..19ec0f05a26 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
@@ -45,8 +45,8 @@ export default {
<p v-html="spendText">
</p>
<a
- class="btn btn-default learn-more-button"
:href="href"
+ class="btn btn-default learn-more-button"
>
{{ __('Learn more') }}
</a>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
index 7d56d2fa5ee..ca3b9338c29 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/time_tracker.vue
@@ -101,8 +101,8 @@ export default {
<template>
<div
- class="time_tracker time-tracking-component-wrap"
v-cloak
+ class="time_tracker time-tracking-component-wrap"
>
<time-tracking-collapsed-state
:show-comparison-state="showComparisonState"
@@ -116,8 +116,8 @@ export default {
<div class="title hide-collapsed">
{{ __('Time tracking') }}
<div
- class="help-button float-right"
v-if="!showHelpState"
+ class="help-button float-right"
@click="toggleHelpState(true)"
>
<i
@@ -127,8 +127,8 @@ export default {
</i>
</div>
<div
- class="close-help-button float-right"
v-if="showHelpState"
+ class="close-help-button float-right"
@click="toggleHelpState(false)"
>
<i
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
index 1608858da22..c44419d24e6 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -118,8 +118,8 @@ export default {
</a>
</template>
<span
- v-if="hasDeploymentTime"
v-tooltip
+ v-if="hasDeploymentTime"
:title="deployment.deployed_at_formatted"
class="js-deploy-time"
>
@@ -127,9 +127,9 @@ export default {
</span>
<loading-button
v-if="deployment.stop_url"
+ :loading="isStopping"
container-class="btn btn-default btn-sm prepend-left-default"
label="Stop environment"
- :loading="isStopping"
@click="stopEnvironment"
/>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
index 8338fde61c7..22c2f74f900 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author.vue
@@ -35,17 +35,17 @@
<template>
<a
:href="authorUrl"
- class="author-link inline"
:v-tooltip="showAuthorTooltip"
:title="author.name"
+ class="author-link inline"
>
<img
:src="avatarUrl"
class="avatar avatar-inline s16"
/>
<span
- class="author"
v-if="showAuthorName"
+ class="author"
>
{{ author.name }}
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
index 644e4b7d81a..ba16cb9e2c8 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_author_time.vue
@@ -1,11 +1,15 @@
<script>
+ import tooltip from '~/vue_shared/directives/tooltip';
import MrWidgetAuthor from './mr_widget_author.vue';
export default {
- name: 'MRWidgetAuthorTime',
+ name: 'MrWidgetAuthorTime',
components: {
MrWidgetAuthor,
},
+ directives: {
+ tooltip,
+ },
props: {
actionText: {
type: String,
@@ -31,9 +35,8 @@
{{ actionText }}
<mr-widget-author :author="author" />
<time
+ v-tooltip
:title="dateTitle"
- data-toggle="tooltip"
- data-placement="top"
data-container="body"
>
{{ dateReadable }}
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
index 51a0fda6555..3ce9d8dc26a 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue
@@ -59,11 +59,11 @@ export default {
<strong>
{{ s__("mrWidget|Request to merge") }}
<span
- class="label-branch js-source-branch"
:class="{ 'label-truncated': isSourceBranchLong }"
:title="isSourceBranchLong ? mr.sourceBranch : ''"
- data-placement="bottom"
:v-tooltip="isSourceBranchLong"
+ class="label-branch js-source-branch"
+ data-placement="bottom"
v-html="mr.sourceBranchLink"
>
</span>
@@ -77,10 +77,10 @@ export default {
{{ s__("mrWidget|into") }}
<span
- class="label-branch"
:v-tooltip="isTargetBranchLong"
:class="{ 'label-truncatedtooltip': isTargetBranchLong }"
:title="isTargetBranchLong ? mr.targetBranch : ''"
+ class="label-branch"
data-placement="bottom"
>
<a
@@ -108,9 +108,9 @@ export default {
{{ s__("mrWidget|Web IDE") }}
</a>
<button
+ :disabled="mr.sourceBranchRemoved"
data-target="#modal_merge_info"
data-toggle="modal"
- :disabled="mr.sourceBranchRemoved"
class="btn btn-sm btn-default inline js-check-out-branch"
type="button"
>
@@ -134,8 +134,8 @@ export default {
<ul class="dropdown-menu dropdown-menu-right">
<li>
<a
- class="js-download-email-patches"
:href="mr.emailPatchesPath"
+ class="js-download-email-patches"
download
>
{{ s__("mrWidget|Email patches") }}
@@ -143,8 +143,8 @@ export default {
</li>
<li>
<a
- class="js-download-plain-diff"
:href="mr.plainDiffPath"
+ class="js-download-plain-diff"
download
>
{{ s__("mrWidget|Plain diff") }}
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 48dff8c4916..2f0b5e12c12 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
@@ -67,8 +67,8 @@ export default {
</template>
<template v-else-if="hasPipeline">
<a
- class="append-right-10"
:href="status.details_path"
+ class="append-right-10"
>
<ci-icon :status="status" />
</a>
@@ -96,8 +96,8 @@ export default {
<span class="mr-widget-pipeline-graph">
<span
- class="stage-cell"
v-if="hasStages"
+ class="stage-cell"
>
<div
v-for="(stage, i) in pipeline.details.stages"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue
index 460437ceeff..56879c04d16 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/source_branch_removal_status.vue
@@ -25,9 +25,9 @@
</span>
<i
v-tooltip
- class="fa fa-question-circle"
:title="tooltipTitle"
:aria-label="tooltipTitle"
+ class="fa fa-question-circle"
>
</i>
</p>
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 b404a592234..2133124347c 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
@@ -39,10 +39,10 @@
{{ s__("mrWidget|This merge request failed to be merged automatically") }}
</span>
<button
- @click="refreshWidget"
:disabled="isRefreshing"
type="button"
class="btn btn-sm btn-default"
+ @click="refreshWidget"
>
<loading-icon
v-if="isRefreshing"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
index caeaac75b45..ae6630dcd6f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_checking.vue
@@ -11,8 +11,8 @@
<template>
<div class="mr-widget-body media">
<status-icon
- status="loading"
:show-disabled-button="true"
+ status="loading"
/>
<div class="media-body space-children">
<span class="bold">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
index 68b691fc914..25a57e520ee 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_closed.vue
@@ -1,11 +1,11 @@
<script>
- import mrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
+ import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
export default {
name: 'MRWidgetClosed',
components: {
- mrWidgetAuthorTime,
+ MrWidgetAuthorTime,
statusIcon,
},
props: {
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
index 5c1500ab801..dff9ec657b9 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_conflicts.vue
@@ -20,8 +20,8 @@
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
index 05fecd4de35..c302960f16e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
@@ -1,5 +1,6 @@
<script>
import { n__ } from '~/locale';
+import { stripHtml } from '~/lib/utils/text_utility';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
@@ -27,6 +28,9 @@ export default {
},
computed: {
+ mergeError() {
+ return this.mr.mergeError ? stripHtml(this.mr.mergeError, ' ').trim() : '';
+ },
timerText() {
return n__(
'Refreshing in a second to show the updated status...',
@@ -76,16 +80,16 @@ export default {
</template>
<template v-else>
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
<span class="bold">
<span
- class="has-error-message"
v-if="mr.mergeError"
+ class="has-error-message"
>
- {{ mr.mergeError }}.
+ {{ mergeError }}.
</span>
<span v-else>
{{ s__("mrWidget|Merge failed.") }}
@@ -97,9 +101,9 @@ export default {
</span>
</span>
<button
- @click="refresh"
class="btn btn-default btn-sm js-refresh-button"
type="button"
+ @click="refresh"
>
{{ s__("mrWidget|Refresh now") }}
</button>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
index e8352c362d6..0d9a560c88e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merge_when_pipeline_succeeds.vue
@@ -90,11 +90,11 @@
</span>
<a
v-if="mr.canCancelAutomaticMerge"
- @click.prevent="cancelAutomaticMerge"
:disabled="isCancellingAutoMerge"
role="button"
href="#"
- class="btn btn-sm btn-default js-cancel-auto-merge">
+ class="btn btn-sm btn-default js-cancel-auto-merge"
+ @click.prevent="cancelAutomaticMerge">
<i
v-if="isCancellingAutoMerge"
class="fa fa-spinner fa-spin"
@@ -127,10 +127,10 @@
<a
v-if="canRemoveSourceBranch"
:disabled="isRemovingSourceBranch"
- @click.prevent="removeSourceBranch"
role="button"
class="btn btn-sm btn-default js-remove-source-branch"
href="#"
+ @click.prevent="removeSourceBranch"
>
<i
v-if="isRemovingSourceBranch"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
index eb581b807a2..985f44dee97 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -4,7 +4,7 @@
import loadingIcon from '~/vue_shared/components/loading_icon.vue';
import { s__, __ } from '~/locale';
import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
- import mrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
+ import MrWidgetAuthorTime from '../../components/mr_widget_author_time.vue';
import statusIcon from '../mr_widget_status_icon.vue';
import eventHub from '../../event_hub';
@@ -14,7 +14,7 @@
tooltip,
},
components: {
- mrWidgetAuthorTime,
+ MrWidgetAuthorTime,
loadingIcon,
statusIcon,
ClipboardButton,
@@ -116,44 +116,44 @@
:date-readable="mr.metrics.readableMergedAt"
/>
<a
- v-if="mr.canRevertInCurrentMR"
v-tooltip
+ v-if="mr.canRevertInCurrentMR"
+ :title="revertTitle"
class="btn btn-close btn-sm"
href="#modal-revert-commit"
data-toggle="modal"
data-container="body"
- :title="revertTitle"
>
{{ revertLabel }}
</a>
<a
- v-else-if="mr.revertInForkPath"
v-tooltip
- class="btn btn-close btn-sm"
- data-method="post"
+ v-else-if="mr.revertInForkPath"
:href="mr.revertInForkPath"
:title="revertTitle"
+ class="btn btn-close btn-sm"
+ data-method="post"
>
{{ revertLabel }}
</a>
<a
- v-if="mr.canCherryPickInCurrentMR"
v-tooltip
+ v-if="mr.canCherryPickInCurrentMR"
+ :title="cherryPickTitle"
class="btn btn-default btn-sm"
href="#modal-cherry-pick-commit"
data-toggle="modal"
data-container="body"
- :title="cherryPickTitle"
>
{{ cherryPickLabel }}
</a>
<a
- v-else-if="mr.cherryPickInForkPath"
v-tooltip
- class="btn btn-default btn-sm"
- data-method="post"
+ v-else-if="mr.cherryPickInForkPath"
:href="mr.cherryPickInForkPath"
:title="cherryPickTitle"
+ class="btn btn-default btn-sm"
+ data-method="post"
>
{{ cherryPickLabel }}
</a>
@@ -186,10 +186,10 @@
>
<span>{{ s__("mrWidget|You can remove source branch now") }}</span>
<button
- @click="removeSourceBranch"
:disabled="isMakingRequest"
type="button"
class="btn btn-sm btn-default js-remove-branch-button"
+ @click="removeSourceBranch"
>
{{ s__("mrWidget|Remove Source Branch") }}
</button>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
index 718c0e4b3c6..b0e96f74626 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_missing_branch.vue
@@ -39,8 +39,8 @@
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
@@ -51,9 +51,9 @@
{{ missingBranchNameMessage }}
<i
v-tooltip
- class="fa fa-question-circle"
:title="message"
:aria-label="message"
+ class="fa fa-question-circle"
>
</i>
</span>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
index e4af50b09f8..92eee2cf5dd 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_not_allowed.vue
@@ -12,8 +12,8 @@
<template>
<div class="mr-widget-body media">
<status-icon
- status="success"
:show-disabled-button="true"
+ status="success"
/>
<div class="media-body space-children">
<span class="bold">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
index 6d7cc03f7ad..37ee5215cea 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_blocked.vue
@@ -11,8 +11,8 @@
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
<span class="bold">
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 143fd328d88..2d8c3d6be87 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
@@ -110,9 +110,9 @@
js-toggle-container accept-action media space-children"
>
<button
+ :disabled="isMakingRequest"
type="button"
class="btn btn-sm btn-reopen btn-success qa-mr-rebase-button"
- :disabled="isMakingRequest"
@click="rebase"
>
<loading-icon v-if="isMakingRequest" />
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
index 875c3323dbb..25c1044fe2b 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_squash_before_merge.vue
@@ -37,23 +37,23 @@ export default {
<div class="accept-control inline">
<label class="merge-param-checkbox">
<input
+ :disabled="isMergeButtonDisabled"
+ v-model="squashBeforeMerge"
type="checkbox"
name="squash"
class="qa-squash-checkbox"
- :disabled="isMergeButtonDisabled"
- v-model="squashBeforeMerge"
@change="updateSquashModel"
/>
{{ __('Squash commits') }}
</label>
<a
+ v-tooltip
:href="mr.squashBeforeMergeHelpPath"
data-title="About this feature"
data-placement="bottom"
target="_blank"
rel="noopener noreferrer nofollow"
data-container="body"
- v-tooltip
>
<icon
name="question-o"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
index 8d55477929f..2bb1a34412e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/pipeline_failed.vue
@@ -12,8 +12,8 @@ export default {
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
<span class="bold">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index 3a194320bd8..fe777a07189 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -235,11 +235,11 @@ export default {
<div class="mr-widget-body-controls media space-children">
<span class="btn-group append-bottom-5">
<button
- @click="handleMergeButtonClick()"
:disabled="isMergeButtonDisabled"
:class="mergeButtonClass"
type="button"
- class="qa-merge-button">
+ class="qa-merge-button"
+ @click="handleMergeButtonClick()">
<i
v-if="isMakingRequest"
class="fa fa-spinner fa-spin"
@@ -265,28 +265,28 @@ export default {
role="menu">
<li>
<a
- @click.prevent="handleMergeButtonClick(true)"
class="merge_when_pipeline_succeeds"
- href="#">
+ href="#"
+ @click.prevent="handleMergeButtonClick(true)">
<span class="media">
<span
- v-html="successSvg"
class="merge-opt-icon"
- aria-hidden="true"></span>
+ aria-hidden="true"
+ v-html="successSvg"></span>
<span class="media-body merge-opt-title">Merge when pipeline succeeds</span>
</span>
</a>
</li>
<li>
<a
- @click.prevent="handleMergeButtonClick(false, true)"
class="accept-merge-request"
- href="#">
+ href="#"
+ @click.prevent="handleMergeButtonClick(false, true)">
<span class="media">
<span
- v-html="warningSvg"
class="merge-opt-icon"
- aria-hidden="true"></span>
+ aria-hidden="true"
+ v-html="warningSvg"></span>
<span class="media-body merge-opt-title">Merge immediately</span>
</span>
</a>
@@ -299,8 +299,8 @@ export default {
<input
id="remove-source-branch-input"
v-model="removeSourceBranch"
- class="js-remove-source-branch-checkbox"
:disabled="isRemoveSourceBranchButtonDisabled"
+ class="js-remove-source-branch-checkbox"
type="checkbox"/> Remove source branch
</label>
@@ -317,10 +317,10 @@ export default {
</span>
<button
v-else
- @click="toggleCommitMessageEditor"
:disabled="isMergeButtonDisabled"
class="js-modify-commit-message-button btn btn-default btn-sm"
- type="button">
+ type="button"
+ @click="toggleCommitMessageEditor">
Modify commit message
</button>
</template>
@@ -356,8 +356,8 @@ export default {
</p>
<div class="hint">
<a
- @click.prevent="updateCommitMessage"
href="#"
+ @click.prevent="updateCommitMessage"
>
{{ commitMessageLinkTitle }}
</a>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
index 7cc07401911..16c903c923f 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/sha_mismatch.vue
@@ -12,8 +12,8 @@ export default {
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
<span class="bold">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
index 8ea3f22ecc2..5eb2058a03b 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/unresolved_discussions.vue
@@ -18,8 +18,8 @@ export default {
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="true"
+ status="warning"
/>
<div class="media-body space-children">
<span class="bold">
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
index fe2608e8212..89c9a41f316 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/work_in_progress.vue
@@ -43,8 +43,8 @@ export default {
<template>
<div class="mr-widget-body media">
<status-icon
- status="warning"
:show-disabled-button="Boolean(mr.removeWIPPath)"
+ status="warning"
/>
<div class="media-body space-children">
<span class="bold">
@@ -60,10 +60,10 @@ export default {
</span>
<button
v-if="mr.removeWIPPath"
- @click="removeWIP"
:disabled="isMakingRequest"
type="button"
- class="btn btn-default btn-xs js-remove-wip">
+ class="btn btn-default btn-sm js-remove-wip"
+ @click="removeWIP">
<i
v-if="isMakingRequest"
class="fa fa-spinner fa-spin"
diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
index c20d07a169d..bc4ba3d050b 100644
--- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue
@@ -36,7 +36,7 @@ import {
notify,
SourceBranchRemovalStatus,
} from './dependencies';
-import { setFavicon } from '../lib/utils/common_utils';
+import { setFaviconOverlay } from '../lib/utils/common_utils';
export default {
el: '#js-vue-mr-widget',
@@ -159,8 +159,9 @@ export default {
},
setFaviconHelper() {
if (this.mr.ciStatusFaviconPath) {
- setFavicon(this.mr.ciStatusFaviconPath);
+ return setFaviconOverlay(this.mr.ciStatusFaviconPath);
}
+ return Promise.resolve();
},
fetchDeployments() {
return this.service.fetchDeployments()
@@ -282,8 +283,8 @@ export default {
/>
</div>
<div
- class="mr-widget-footer"
v-if="shouldRenderMergeHelp"
+ class="mr-widget-footer"
>
<mr-widget-merge-help />
</div>
diff --git a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
index 0d64efcbf68..a2518e2a611 100644
--- a/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_badge_link.vue
@@ -50,9 +50,9 @@ export default {
</script>
<template>
<a
+ v-tooltip
:href="status.details_path"
:class="cssClass"
- v-tooltip
:title="!showText ? status.text : ''"
>
<ci-icon :status="status" />
diff --git a/app/assets/javascripts/vue_shared/components/clipboard_button.vue b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
index cb2cc3901ad..dc5760bce28 100644
--- a/app/assets/javascripts/vue_shared/components/clipboard_button.vue
+++ b/app/assets/javascripts/vue_shared/components/clipboard_button.vue
@@ -49,14 +49,14 @@ export default {
<template>
<button
- type="button"
- class="btn"
+ v-tooltip
:class="cssClass"
:title="title"
:data-clipboard-text="text"
- v-tooltip
:data-container="tooltipContainer"
:data-placement="tooltipPlacement"
+ type="button"
+ class="btn"
>
<i
aria-hidden="true"
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index 8f250a6c989..13bca99dcb3 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -124,11 +124,11 @@ export default {
</div>
<a
- class="ref-name"
- :href="commitRef.ref_url"
v-tooltip
- data-container="body"
+ :href="commitRef.ref_url"
:title="commitRef.name"
+ class="ref-name"
+ data-container="body"
>
{{ commitRef.name }}
</a>
@@ -139,8 +139,8 @@ export default {
/>
<a
- class="commit-sha"
:href="commitUrl"
+ class="commit-sha"
>
{{ shortSha }}
</a>
@@ -152,15 +152,15 @@ export default {
>
<user-avatar-link
v-if="hasAuthor"
- class="avatar-image-container"
:link-href="author.path"
:img-src="author.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="author.username"
+ class="avatar-image-container"
/>
<a
- class="commit-row-message"
:href="commitUrl"
+ class="commit-row-message"
>
{{ title }}
</a>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
index 7b5367ac19b..f1ef50d0e3d 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/download_viewer.vue
@@ -32,7 +32,10 @@ export default {
<div class="file-container">
<div class="file-content">
<p class="prepend-top-10 file-info">
- {{ fileName }} ({{ fileSizeReadable }})
+ {{ fileName }}
+ <template v-if="fileSize > 0">
+ ({{ fileSizeReadable }})
+ </template>
</p>
<a
:href="path"
@@ -41,9 +44,9 @@ export default {
download
target="_blank">
<icon
+ :size="16"
name="download"
css-classes="float-left append-right-8"
- :size="16"
/>
{{ __('Download') }}
</a>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
index a5999f909ca..6851029018a 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/image_viewer.vue
@@ -1,4 +1,5 @@
<script>
+import _ from 'underscore';
import { numberToHumanSize } from '../../../../lib/utils/number_utils';
export default {
@@ -12,6 +13,10 @@ export default {
required: false,
default: 0,
},
+ renderInfo: {
+ type: Boolean,
+ default: true,
+ },
},
data() {
return {
@@ -26,14 +31,34 @@ export default {
return numberToHumanSize(this.fileSize);
},
},
+ beforeDestroy() {
+ window.removeEventListener('resize', this.resizeThrottled, false);
+ },
+ mounted() {
+ // The onImgLoad may have happened before the control was actually mounted
+ this.onImgLoad();
+ this.resizeThrottled = _.throttle(this.onImgLoad, 400);
+ window.addEventListener('resize', this.resizeThrottled, false);
+ },
methods: {
onImgLoad() {
const contentImg = this.$refs.contentImg;
- this.isZoomable =
- contentImg.naturalWidth > contentImg.width || contentImg.naturalHeight > contentImg.height;
- this.width = contentImg.naturalWidth;
- this.height = contentImg.naturalHeight;
+ if (contentImg) {
+ this.isZoomable =
+ contentImg.naturalWidth > contentImg.width ||
+ contentImg.naturalHeight > contentImg.height;
+
+ this.width = contentImg.naturalWidth;
+ this.height = contentImg.naturalHeight;
+
+ this.$emit('imgLoaded', {
+ width: this.width,
+ height: this.height,
+ renderedWidth: contentImg.clientWidth,
+ renderedHeight: contentImg.clientHeight,
+ });
+ }
},
onImgClick() {
if (this.isZoomable) this.isZoomed = !this.isZoomed;
@@ -47,20 +72,22 @@ export default {
<div class="file-content image_file">
<img
ref="contentImg"
- :class="{ 'isZoomable': isZoomable, 'isZoomed': isZoomed }"
+ :class="{ 'is-zoomable': isZoomable, 'is-zoomed': isZoomed }"
:src="path"
:alt="path"
@load="onImgLoad"
@click="onImgClick"/>
- <p class="file-info prepend-top-10">
+ <p
+ v-if="renderInfo"
+ class="file-info prepend-top-10">
<template v-if="fileSize>0">
{{ fileSizeReadable }}
</template>
<template v-if="fileSize>0 && width && height">
- -
+ |
</template>
<template v-if="width && height">
- {{ width }} x {{ height }}
+ W: {{ width }} | H: {{ height }}
</template>
</p>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
index 424af5a0293..9c1e5c68649 100644
--- a/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/deprecated_modal.vue
@@ -86,8 +86,8 @@
<div class="modal-open">
<div
:id="id"
- class="modal"
:class="id ? '' : 'd-block'"
+ class="modal"
role="dialog"
tabindex="-1"
>
@@ -105,9 +105,9 @@
<button
type="button"
class="close float-right"
- @click="emitCancel($event)"
data-dismiss="modal"
aria-label="Close"
+ @click="emitCancel($event)"
>
<span aria-hidden="true">&times;</span>
</button>
@@ -115,22 +115,22 @@
</div>
<div class="modal-body">
<slot
- name="body"
:text="text"
+ name="body"
>
<p>{{ text }}</p>
</slot>
</div>
<div
- class="modal-footer"
v-if="!hideFooter"
+ class="modal-footer"
>
<button
+ :class="btnCancelKindClass"
type="button"
class="btn"
- :class="btnCancelKindClass"
- @click="emitCancel($event)"
data-dismiss="modal"
+ @click="emitCancel($event)"
>
{{ closeButtonLabel }}
</button>
@@ -151,12 +151,12 @@
<button
v-if="primaryButtonLabel"
- type="button"
- class="btn js-primary-button"
:disabled="submitDisabled"
:class="btnKindClass"
- @click="emitSubmit($event)"
+ type="button"
+ class="btn js-primary-button"
data-dismiss="modal"
+ @click="emitSubmit($event)"
>
{{ primaryButtonLabel }}
</button>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/constants.js b/app/assets/javascripts/vue_shared/components/diff_viewer/constants.js
new file mode 100644
index 00000000000..6c1840361af
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/constants.js
@@ -0,0 +1,12 @@
+export const diffModes = {
+ replaced: 'replaced',
+ new: 'new',
+ deleted: 'deleted',
+ renamed: 'renamed',
+};
+
+export const imageViewMode = {
+ twoup: 'twoup',
+ swipe: 'swipe',
+ onion: 'onion',
+};
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
new file mode 100644
index 00000000000..2c47f5b9b35
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/diff_viewer.vue
@@ -0,0 +1,70 @@
+<script>
+import { viewerInformationForPath } from '../content_viewer/lib/viewer_utils';
+import ImageDiffViewer from './viewers/image_diff_viewer.vue';
+import DownloadDiffViewer from './viewers/download_diff_viewer.vue';
+
+export default {
+ props: {
+ diffMode: {
+ type: String,
+ required: true,
+ },
+ newPath: {
+ type: String,
+ required: true,
+ },
+ newSha: {
+ type: String,
+ required: true,
+ },
+ oldPath: {
+ type: String,
+ required: true,
+ },
+ oldSha: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ viewer() {
+ if (!this.newPath) return null;
+
+ const previewInfo = viewerInformationForPath(this.newPath);
+ if (!previewInfo) return DownloadDiffViewer;
+
+ switch (previewInfo.id) {
+ case 'image':
+ return ImageDiffViewer;
+ default:
+ return DownloadDiffViewer;
+ }
+ },
+ fullOldPath() {
+ return `${gon.relative_url_root}/${this.projectPath}/raw/${this.oldSha}/${this.oldPath}`;
+ },
+ fullNewPath() {
+ return `${gon.relative_url_root}/${this.projectPath}/raw/${this.newSha}/${this.newPath}`;
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ v-if="viewer"
+ class="diff-file preview-container">
+ <component
+ :is="viewer"
+ :diff-mode="diffMode"
+ :new-path="fullNewPath"
+ :old-path="fullOldPath"
+ :project-path="projectPath"
+ />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/download_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/download_diff_viewer.vue
new file mode 100644
index 00000000000..50389b6ae63
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/download_diff_viewer.vue
@@ -0,0 +1,69 @@
+<script>
+import DownloadViewer from '../../content_viewer/viewers/download_viewer.vue';
+import { diffModes } from '../constants';
+
+export default {
+ components: {
+ DownloadViewer,
+ },
+ props: {
+ diffMode: {
+ type: String,
+ required: true,
+ },
+ newPath: {
+ type: String,
+ required: true,
+ },
+ oldPath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ diffModes,
+};
+</script>
+
+<template>
+ <div class="diff-file-container">
+ <div class="diff-viewer">
+ <div
+ v-if="diffMode === $options.diffModes.replaced"
+ class="two-up view row">
+ <div class="col-sm-6 deleted">
+ <download-viewer
+ :path="oldPath"
+ :project-path="projectPath"
+ />
+ </div>
+ <div class="col-sm-6 added">
+ <download-viewer
+ :path="newPath"
+ :project-path="projectPath"
+ />
+ </div>
+ </div>
+ <div
+ v-else-if="diffMode === $options.diffModes.new"
+ class="added">
+ <download-viewer
+ :path="newPath"
+ :project-path="projectPath"
+ />
+ </div>
+ <div
+ v-else
+ class="deleted">
+ <download-viewer
+ :path="oldPath"
+ :project-path="projectPath"
+ />
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
new file mode 100644
index 00000000000..38e881d17a2
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/onion_skin_viewer.vue
@@ -0,0 +1,160 @@
+<script>
+import { pixeliseValue } from '../../../lib/utils/dom_utils';
+import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
+
+export default {
+ components: {
+ ImageViewer,
+ },
+ props: {
+ newPath: {
+ type: String,
+ required: true,
+ },
+ oldPath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ onionMaxWidth: undefined,
+ onionMaxHeight: undefined,
+ onionOldImgInfo: null,
+ onionNewImgInfo: null,
+ onionDraggerPos: 0,
+ onionOpacity: 1,
+ dragging: false,
+ };
+ },
+ computed: {
+ onionMaxPixelWidth() {
+ return pixeliseValue(this.onionMaxWidth);
+ },
+ onionMaxPixelHeight() {
+ return pixeliseValue(this.onionMaxHeight);
+ },
+ onionDraggerPixelPos() {
+ return pixeliseValue(this.onionDraggerPos);
+ },
+ },
+ beforeDestroy() {
+ document.body.removeEventListener('mouseup', this.stopDrag);
+ this.$refs.dragger.removeEventListener('mousedown', this.startDrag);
+ },
+ methods: {
+ dragMove(e) {
+ if (!this.dragging) return;
+ const left = e.pageX - this.$refs.dragTrack.getBoundingClientRect().left;
+ const dragTrackWidth =
+ this.$refs.dragTrack.clientWidth - this.$refs.dragger.clientWidth || 100;
+
+ let leftValue = left;
+ if (leftValue < 0) leftValue = 0;
+ if (leftValue > dragTrackWidth) leftValue = dragTrackWidth;
+
+ this.onionOpacity = left / dragTrackWidth;
+ this.onionDraggerPos = leftValue;
+ },
+ startDrag() {
+ this.dragging = true;
+ document.body.style.userSelect = 'none';
+ document.body.addEventListener('mousemove', this.dragMove);
+ },
+ stopDrag() {
+ this.dragging = false;
+ document.body.style.userSelect = '';
+ document.body.removeEventListener('mousemove', this.dragMove);
+ },
+ prepareOnionSkin() {
+ if (this.onionOldImgInfo && this.onionNewImgInfo) {
+ this.onionMaxWidth = Math.max(
+ this.onionOldImgInfo.renderedWidth,
+ this.onionNewImgInfo.renderedWidth,
+ );
+ this.onionMaxHeight = Math.max(
+ this.onionOldImgInfo.renderedHeight,
+ this.onionNewImgInfo.renderedHeight,
+ );
+
+ this.onionOpacity = 1;
+ this.onionDraggerPos =
+ this.$refs.dragTrack.clientWidth - this.$refs.dragger.clientWidth || 100;
+
+ document.body.addEventListener('mouseup', this.stopDrag);
+ }
+ },
+ onionNewImgLoaded(imgInfo) {
+ this.onionNewImgInfo = imgInfo;
+ this.prepareOnionSkin();
+ },
+ onionOldImgLoaded(imgInfo) {
+ this.onionOldImgInfo = imgInfo;
+ this.prepareOnionSkin();
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="onion-skin view">
+ <div
+ :style="{
+ 'width': onionMaxPixelWidth,
+ 'height': onionMaxPixelHeight,
+ 'user-select': dragging === true ? 'none' : '',
+ }"
+ class="onion-skin-frame">
+ <div
+ :style="{
+ 'width': onionMaxPixelWidth,
+ 'height': onionMaxPixelHeight,
+ }"
+ class="frame deleted">
+ <image-viewer
+ key="onionOldImg"
+ :render-info="false"
+ :path="oldPath"
+ :project-path="projectPath"
+ @imgLoaded="onionOldImgLoaded"
+ />
+ </div>
+ <div
+ ref="addedFrame"
+ :style="{
+ 'opacity': onionOpacity,
+ 'width': onionMaxPixelWidth,
+ 'height': onionMaxPixelHeight,
+ }"
+ class="added frame">
+ <image-viewer
+ key="onionNewImg"
+ :render-info="false"
+ :path="newPath"
+ :project-path="projectPath"
+ @imgLoaded="onionNewImgLoaded"
+ />
+ </div>
+ <div class="controls">
+ <div class="transparent"></div>
+ <div
+ ref="dragTrack"
+ class="drag-track"
+ @mousedown="startDrag"
+ @mouseup="stopDrag">
+ <div
+ ref="dragger"
+ :style="{ 'left': onionDraggerPixelPos }"
+ class="dragger">
+ </div>
+ </div>
+ <div class="opaque"></div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
new file mode 100644
index 00000000000..86366c799a2
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/swipe_viewer.vue
@@ -0,0 +1,158 @@
+<script>
+import _ from 'underscore';
+import { pixeliseValue } from '../../../lib/utils/dom_utils';
+import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
+
+export default {
+ components: {
+ ImageViewer,
+ },
+ props: {
+ newPath: {
+ type: String,
+ required: true,
+ },
+ oldPath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ dragging: false,
+ swipeOldImgInfo: null,
+ swipeNewImgInfo: null,
+ swipeMaxWidth: undefined,
+ swipeMaxHeight: undefined,
+ swipeBarPos: 1,
+ swipeWrapWidth: undefined,
+ };
+ },
+ computed: {
+ swipeMaxPixelWidth() {
+ return pixeliseValue(this.swipeMaxWidth);
+ },
+ swipeMaxPixelHeight() {
+ return pixeliseValue(this.swipeMaxHeight);
+ },
+ swipeWrapPixelWidth() {
+ return pixeliseValue(this.swipeWrapWidth);
+ },
+ swipeBarPixelPos() {
+ return pixeliseValue(this.swipeBarPos);
+ },
+ },
+ beforeDestroy() {
+ window.removeEventListener('resize', this.resizeThrottled, false);
+ document.body.removeEventListener('mouseup', this.stopDrag);
+ document.body.removeEventListener('mousemove', this.dragMove);
+ },
+ mounted() {
+ window.addEventListener('resize', this.resize, false);
+ },
+ methods: {
+ dragMove(e) {
+ if (!this.dragging) return;
+
+ let leftValue = e.pageX - this.$refs.swipeFrame.getBoundingClientRect().left;
+ const spaceLeft = 20;
+ const { clientWidth } = this.$refs.swipeFrame;
+ if (leftValue <= 0) {
+ leftValue = 0;
+ } else if (leftValue > clientWidth - spaceLeft) {
+ leftValue = clientWidth - spaceLeft;
+ }
+
+ this.swipeWrapWidth = this.swipeMaxWidth - leftValue;
+ this.swipeBarPos = leftValue;
+ },
+ startDrag() {
+ this.dragging = true;
+ document.body.style.userSelect = 'none';
+ document.body.addEventListener('mousemove', this.dragMove);
+ },
+ stopDrag() {
+ this.dragging = false;
+ document.body.style.userSelect = '';
+ document.body.removeEventListener('mousemove', this.dragMove);
+ },
+ prepareSwipe() {
+ if (this.swipeOldImgInfo && this.swipeNewImgInfo) {
+ // Add 2 for border width
+ this.swipeMaxWidth =
+ Math.max(this.swipeOldImgInfo.renderedWidth, this.swipeNewImgInfo.renderedWidth) + 2;
+ this.swipeWrapWidth = this.swipeMaxWidth;
+ this.swipeMaxHeight =
+ Math.max(this.swipeOldImgInfo.renderedHeight, this.swipeNewImgInfo.renderedHeight) + 2;
+
+ document.body.addEventListener('mouseup', this.stopDrag);
+ }
+ },
+ swipeNewImgLoaded(imgInfo) {
+ this.swipeNewImgInfo = imgInfo;
+ this.prepareSwipe();
+ },
+ swipeOldImgLoaded(imgInfo) {
+ this.swipeOldImgInfo = imgInfo;
+ this.prepareSwipe();
+ },
+ resize: _.throttle(function throttledResize() {
+ this.swipeBarPos = 0;
+ }, 400),
+ },
+};
+</script>
+
+<template>
+ <div class="swipe view">
+ <div
+ ref="swipeFrame"
+ :style="{
+ 'width': swipeMaxPixelWidth,
+ 'height': swipeMaxPixelHeight,
+ }"
+ class="swipe-frame">
+ <div class="frame deleted">
+ <image-viewer
+ key="swipeOldImg"
+ ref="swipeOldImg"
+ :render-info="false"
+ :path="oldPath"
+ :project-path="projectPath"
+ @imgLoaded="swipeOldImgLoaded"
+ />
+ </div>
+ <div
+ ref="swipeWrap"
+ :style="{
+ 'width': swipeWrapPixelWidth,
+ 'height': swipeMaxPixelHeight,
+ }"
+ class="swipe-wrap">
+ <div class="frame added">
+ <image-viewer
+ key="swipeNewImg"
+ :render-info="false"
+ :path="newPath"
+ :project-path="projectPath"
+ @imgLoaded="swipeNewImgLoaded"
+ />
+ </div>
+ </div>
+ <span
+ ref="swipeBar"
+ :style="{ 'left': swipeBarPixelPos }"
+ class="swipe-bar"
+ @mousedown="startDrag"
+ @mouseup="stopDrag">
+ <span class="top-handle"></span>
+ <span class="bottom-handle"></span>
+ </span>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
new file mode 100644
index 00000000000..9c19266ecdf
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff/two_up_viewer.vue
@@ -0,0 +1,41 @@
+<script>
+import ImageViewer from '../../../content_viewer/viewers/image_viewer.vue';
+
+export default {
+ components: {
+ ImageViewer,
+ },
+ props: {
+ newPath: {
+ type: String,
+ required: true,
+ },
+ oldPath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="two-up view row">
+ <div class="col-sm-6 frame deleted">
+ <image-viewer
+ :path="oldPath"
+ :project-path="projectPath"
+ />
+ </div>
+ <div class="col-sm-6 frame added">
+ <image-viewer
+ :path="newPath"
+ :project-path="projectPath"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
new file mode 100644
index 00000000000..1af85283277
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue
@@ -0,0 +1,109 @@
+<script>
+import ImageViewer from '../../content_viewer/viewers/image_viewer.vue';
+import TwoUpViewer from './image_diff/two_up_viewer.vue';
+import SwipeViewer from './image_diff/swipe_viewer.vue';
+import OnionSkinViewer from './image_diff/onion_skin_viewer.vue';
+import { diffModes, imageViewMode } from '../constants';
+
+export default {
+ components: {
+ ImageViewer,
+ TwoUpViewer,
+ SwipeViewer,
+ OnionSkinViewer,
+ },
+ props: {
+ diffMode: {
+ type: String,
+ required: true,
+ },
+ newPath: {
+ type: String,
+ required: true,
+ },
+ oldPath: {
+ type: String,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ data() {
+ return {
+ mode: imageViewMode.twoup,
+ };
+ },
+ methods: {
+ changeMode(newMode) {
+ this.mode = newMode;
+ },
+ },
+ diffModes,
+ imageViewMode,
+};
+</script>
+
+<template>
+ <div class="diff-file-container">
+ <div
+ v-if="diffMode === $options.diffModes.replaced"
+ class="diff-viewer">
+ <div class="image js-replaced-image">
+ <two-up-viewer
+ v-if="mode === $options.imageViewMode.twoup"
+ v-bind="$props"/>
+ <swipe-viewer
+ v-else-if="mode === $options.imageViewMode.swipe"
+ v-bind="$props"/>
+ <onion-skin-viewer
+ v-else-if="mode === $options.imageViewMode.onion"
+ v-bind="$props"/>
+ </div>
+ <div class="view-modes">
+ <ul class="view-modes-menu">
+ <li
+ :class="{
+ active: mode === $options.imageViewMode.twoup
+ }"
+ @click="changeMode($options.imageViewMode.twoup)">
+ {{ s__('ImageDiffViewer|2-up') }}
+ </li>
+ <li
+ :class="{
+ active: mode === $options.imageViewMode.swipe
+ }"
+ @click="changeMode($options.imageViewMode.swipe)">
+ {{ s__('ImageDiffViewer|Swipe') }}
+ </li>
+ <li
+ :class="{
+ active: mode === $options.imageViewMode.onion
+ }"
+ @click="changeMode($options.imageViewMode.onion)">
+ {{ s__('ImageDiffViewer|Onion skin') }}
+ </li>
+ </ul>
+ </div>
+ <div class="note-container"></div>
+ </div>
+ <div
+ v-else-if="diffMode === $options.diffModes.new"
+ class="diff-viewer added">
+ <image-viewer
+ :path="newPath"
+ :project-path="projectPath"
+ />
+ </div>
+ <div
+ v-else
+ class="diff-viewer deleted">
+ <image-viewer
+ :path="oldPath"
+ :project-path="projectPath"
+ />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
index c159333d89a..3cba0c5e633 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_button.vue
@@ -28,11 +28,11 @@ export default {
<template>
<button
+ :disabled="isDisabled || isLoading"
class="dropdown-menu-toggle dropdown-menu-full-width"
type="button"
data-toggle="dropdown"
aria-expanded="false"
- :disabled="isDisabled || isLoading"
>
<loading-icon
v-show="isLoading"
@@ -42,8 +42,8 @@ export default {
{{ toggleText }}
</span>
<span
- class="dropdown-toggle-icon"
v-show="!isLoading"
+ class="dropdown-toggle-icon"
>
<i
class="fa fa-chevron-down"
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue
index 1fe27eb97ab..b7a4613bdd2 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue
@@ -15,8 +15,8 @@ export default {
<template>
<input
- type="hidden"
:name="name"
:value="value"
+ type="hidden"
/>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
index c2145a26e64..7f1912f6405 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
@@ -23,10 +23,10 @@ export default {
<template>
<div class="dropdown-input">
<input
- class="dropdown-input-field"
- type="search"
v-model="searchQuery"
:placeholder="placeholderText"
+ class="dropdown-input-field"
+ type="search"
autocomplete="off"
/>
<i
diff --git a/app/assets/javascripts/vue_shared/components/expand_button.vue b/app/assets/javascripts/vue_shared/components/expand_button.vue
index 9295be3e2b2..0fdea651130 100644
--- a/app/assets/javascripts/vue_shared/components/expand_button.vue
+++ b/app/assets/javascripts/vue_shared/components/expand_button.vue
@@ -32,10 +32,10 @@ export default {
<template>
<span>
<button
- type="button"
v-show="isCollapsed"
- class="text-expander btn-blank"
:aria-label="ariaLabel"
+ type="button"
+ class="text-expander btn-blank"
@click="onClick">
...
</button>
diff --git a/app/assets/javascripts/vue_shared/components/file_icon.vue b/app/assets/javascripts/vue_shared/components/file_icon.vue
index be2755452e2..878c805ada5 100644
--- a/app/assets/javascripts/vue_shared/components/file_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/file_icon.vue
@@ -73,8 +73,8 @@ export default {
<template>
<span>
<svg
- :class="[iconSizeClass, cssClasses]"
v-if="!loading && !folder"
+ :class="[iconSizeClass, cssClasses]"
>
<use v-bind="{ 'xlink:href':spriteHref }" />
</svg>
diff --git a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
index 9ffbaae3ea5..9bca1993ccc 100644
--- a/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
+++ b/app/assets/javascripts/vue_shared/components/file_icon/file_icon_map.js
@@ -513,7 +513,7 @@ const fileNameIcons = {
'credits.md': 'credits',
'credits.md.rendered': 'credits',
'.flowconfig': 'flow',
- 'favicon.ico': 'favicon',
+ 'favicon.png': 'favicon',
'karma.conf.js': 'karma',
'karma.conf.ts': 'karma',
'karma.conf.coffee': 'karma',
diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue
index 7ba58bd5959..c03a342e777 100644
--- a/app/assets/javascripts/vue_shared/components/gl_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue
@@ -57,8 +57,8 @@ export default {
role="dialog"
>
<div
- class="modal-dialog"
:class="modalSizeClass"
+ class="modal-dialog"
role="document"
>
<div class="modal-content">
@@ -70,10 +70,10 @@ export default {
</slot>
</h4>
<button
+ :aria-label="s__('Modal|Close')"
type="button"
class="close js-modal-close-action"
data-dismiss="modal"
- :aria-label="s__('Modal|Close')"
@click="emitCancel($event)"
>
<span aria-hidden="true">&times;</span>
@@ -96,9 +96,9 @@ export default {
{{ s__('Modal|Cancel') }}
</button>
<button
+ :class="`btn-${footerPrimaryButtonVariant}`"
type="button"
class="btn js-modal-primary-action"
- :class="`btn-${footerPrimaryButtonVariant}`"
data-dismiss="modal"
@click="emitSubmit($event)"
>
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 ca17fa06a00..62d35f6547d 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -117,8 +117,8 @@ export default {
</section>
<section
- class="header-action-buttons"
v-if="actions.length"
+ class="header-action-buttons"
>
<template
v-for="(action, i) in actions"
@@ -135,21 +135,21 @@ export default {
<a
v-else-if="action.type === 'ujs-link'"
:href="action.path"
- data-method="post"
- rel="nofollow"
:class="action.cssClass"
:key="i"
+ data-method="post"
+ rel="nofollow"
>
{{ action.label }}
</a>
<button
v-else-if="action.type === 'button'"
- @click="onClickAction(action)"
:disabled="action.isLoading"
:class="action.cssClass"
- type="button"
:key="i"
+ type="button"
+ @click="onClickAction(action)"
>
{{ action.label }}
<i
@@ -162,11 +162,11 @@ export default {
</template>
<button
v-if="hasSidebarButton"
+ id="toggleSidebar"
type="button"
class="btn btn-default d-block d-sm-none d-md-none
sidebar-toggle-btn js-sidebar-build-toggle js-sidebar-build-toggle-header"
aria-label="Toggle Sidebar"
- id="toggleSidebar"
>
<i
class="fa fa-angle-double-left"
diff --git a/app/assets/javascripts/vue_shared/components/identicon.vue b/app/assets/javascripts/vue_shared/components/identicon.vue
index 23010f40f26..4ffc811e714 100644
--- a/app/assets/javascripts/vue_shared/components/identicon.vue
+++ b/app/assets/javascripts/vue_shared/components/identicon.vue
@@ -43,9 +43,9 @@ export default {
<template>
<div
- class="avatar identicon"
:class="sizeClass"
- :style="identiconStyles">
+ :style="identiconStyles"
+ class="avatar identicon">
{{ identiconTitle }}
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
index 3d39b3ab173..ca8ce554588 100644
--- a/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/issue_warning.vue
@@ -33,11 +33,11 @@
<template>
<div class="issuable-note-warning">
<icon
+ v-if="!isLockedAndConfidential"
:name="warningIcon"
:size="16"
class="icon inline"
aria-hidden="true"
- v-if="!isLockedAndConfidential"
/>
<span v-if="isLockedAndConfidential">
diff --git a/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js b/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js
new file mode 100644
index 00000000000..02f28da8bb0
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/lib/utils/dom_utils.js
@@ -0,0 +1,5 @@
+export function pixeliseValue(val) {
+ return val ? `${val}px` : '';
+}
+
+export default {};
diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue
index 88c13a1f340..2ff0c056b9c 100644
--- a/app/assets/javascripts/vue_shared/components/loading_button.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_button.vue
@@ -54,19 +54,19 @@
<template>
<button
- @click="onClick"
- type="button"
:class="containerClass"
:disabled="loading || disabled"
+ type="button"
+ @click="onClick"
>
<transition name="fade">
<loading-icon
v-if="loading"
:inline="true"
- class="js-loading-button-icon"
:class="{
'append-right-5': label
}"
+ class="js-loading-button-icon"
/>
</transition>
<transition name="fade">
diff --git a/app/assets/javascripts/vue_shared/components/loading_icon.vue b/app/assets/javascripts/vue_shared/components/loading_icon.vue
index 12a75e016d7..db22c5f02cd 100644
--- a/app/assets/javascripts/vue_shared/components/loading_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_icon.vue
@@ -35,10 +35,10 @@
:is="rootElementType"
class="loading-container text-center">
<i
- class="fa fa-spin fa-spinner"
:class="cssClass"
- aria-hidden="true"
:aria-label="label"
+ class="fa fa-spin fa-spinner"
+ aria-hidden="true"
>
</i>
</component>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 12c7d125062..05e8ed2da2c 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -117,17 +117,17 @@
<template>
<div
- class="md-area js-vue-markdown-field"
+ ref="gl-form"
:class="{ 'prepend-top-default append-bottom-default': addSpacingClasses }"
- ref="gl-form">
+ class="md-area js-vue-markdown-field">
<markdown-header
:preview-markdown="previewMarkdown"
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab"
/>
<div
- class="md-write-holder"
v-show="!previewMarkdown"
+ class="md-write-holder"
>
<div class="zen-backdrop">
<slot name="textarea"></slot>
@@ -137,8 +137,8 @@
aria-label="Enter zen mode"
>
<icon
- name="screen-normal"
:size="32"
+ name="screen-normal"
/>
</a>
<markdown-toolbar
@@ -149,8 +149,8 @@
</div>
</div>
<div
- class="md md-preview-holder md-preview"
v-show="previewMarkdown"
+ class="md md-preview-holder md-preview"
>
<div
ref="markdown-preview"
@@ -164,8 +164,8 @@
<template v-if="previewMarkdown && !markdownPreviewLoading">
<div
v-if="referencedCommands"
- v-html="referencedCommands"
class="referenced-commands"
+ v-html="referencedCommands"
>
</div>
<div
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index db453c30576..ee3628b1e3f 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -54,8 +54,8 @@
<div class="md-header">
<ul class="nav-links clearfix">
<li
- class="md-header-tab"
:class="{ active: !previewMarkdown }"
+ class="md-header-tab"
>
<a
class="js-write-link"
@@ -67,8 +67,8 @@
</a>
</li>
<li
- class="md-header-tab"
:class="{ active: previewMarkdown }"
+ class="md-header-tab"
>
<a
class="js-preview-link"
@@ -80,8 +80,8 @@
</a>
</li>
<li
- class="md-header-toolbar"
:class="{ active: !previewMarkdown }"
+ class="md-header-toolbar"
>
<toolbar-button
tag="**"
@@ -94,8 +94,8 @@
icon="italic"
/>
<toolbar-button
- tag="> "
:prepend="true"
+ tag="> "
button-title="Insert a quote"
icon="quote"
/>
@@ -106,20 +106,20 @@
icon="code"
/>
<toolbar-button
- tag="* "
:prepend="true"
+ tag="* "
button-title="Add a bullet list"
icon="list-bulleted"
/>
<toolbar-button
- tag="1. "
:prepend="true"
+ tag="1. "
button-title="Add a numbered list"
icon="list-numbered"
/>
<toolbar-button
- tag="* [ ] "
:prepend="true"
+ tag="* [ ] "
button-title="Add a task list"
icon="task-done"
/>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
index 2d2d69ebeb2..9f1e009efdd 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar_button.vue
@@ -39,15 +39,15 @@
<template>
<button
v-tooltip
- type="button"
- class="toolbar-btn js-md"
- tabindex="-1"
- data-container="body"
:data-md-tag="tag"
:data-md-block="tagBlock"
:data-md-prepend="prepend"
:title="buttonTitle"
:aria-label="buttonTitle"
+ type="button"
+ class="toolbar-btn js-md"
+ tabindex="-1"
+ data-container="body"
>
<icon
:name="icon"
diff --git a/app/assets/javascripts/vue_shared/components/memory_graph.vue b/app/assets/javascripts/vue_shared/components/memory_graph.vue
index b07f6b07afe..522091ea889 100644
--- a/app/assets/javascripts/vue_shared/components/memory_graph.vue
+++ b/app/assets/javascripts/vue_shared/components/memory_graph.vue
@@ -113,19 +113,19 @@ export default {
<template>
<div class="memory-graph-container">
<svg
- class="has-tooltip"
:title="getFormattedMedian"
:width="width"
:height="height"
+ class="has-tooltip"
xmlns="http://www.w3.org/2000/svg">
<path
:d="pathD"
:viewBox="pathViewBox"
/>
<circle
- r="1.5"
:cx="dotX"
:cy="dotY"
+ r="1.5"
tranform="translate(0 -1)"
/>
</svg>
diff --git a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
index 08d4936f480..99d61b5639d 100644
--- a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
+++ b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
@@ -59,9 +59,9 @@ export default {
}"
>
<a
+ :class="`js-${scope}-tab-${tab.scope}`"
role="button"
@click="onTabClick(tab)"
- :class="`js-${scope}-tab-${tab.scope}`"
>
{{ tab.name }}
diff --git a/app/assets/javascripts/vue_shared/components/panel_resizer.vue b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
index abbe9a22717..8c2dcc2d902 100644
--- a/app/assets/javascripts/vue_shared/components/panel_resizer.vue
+++ b/app/assets/javascripts/vue_shared/components/panel_resizer.vue
@@ -82,9 +82,9 @@
<template>
<div
- class="dragHandle"
:class="className"
:style="cursorStyle"
+ class="dragHandle"
@mousedown="startDrag"
@dblclick="resetSize"
></div>
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
index 279cc1de5bb..97ca4d93bd7 100644
--- a/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
+++ b/app/assets/javascripts/vue_shared/components/project_avatar/image.vue
@@ -85,7 +85,6 @@
<template>
<img
v-tooltip
- class="avatar"
:class="{
lazy: lazy,
[avatarSizeClass]: true,
@@ -99,5 +98,6 @@
:data-container="tooltipContainer"
:data-placement="tooltipPlacement"
:title="tooltipText"
+ class="avatar"
/>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
index 21ffdc1dc86..a2a9a5e6987 100644
--- a/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/recaptcha_modal.vue
@@ -66,10 +66,10 @@
<template>
<deprecated-modal
- kind="warning"
- class="recaptcha-modal js-recaptcha-modal"
:hide-footer="true"
:title="__('Please solve the reCAPTCHA')"
+ kind="warning"
+ class="recaptcha-modal js-recaptcha-modal"
@cancel="close"
>
<div slot="body">
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
index 71ec34f2c7a..74998a4787d 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/date_picker.vue
@@ -97,8 +97,8 @@
<template>
<div
- class="block"
:class="blockClass"
+ class="block"
>
<div class="issuable-sidebar-header">
<toggle-sidebar
@@ -107,8 +107,8 @@
/>
</div>
<collapsed-calendar-icon
- class="sidebar-collapsed-icon"
:text="collapsedText"
+ class="sidebar-collapsed-icon"
/>
<div class="title">
{{ label }}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
index f155ac2be02..a3fc358130f 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/base.vue
@@ -143,8 +143,8 @@ export default {
:value="label.id"
/>
<div
- class="dropdown"
ref="dropdown"
+ class="dropdown"
>
<dropdown-button
:ability-name="abilityName"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue
index 47497c1de98..48d2f16f554 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_button.vue
@@ -53,10 +53,7 @@ export default {
<template>
<button
- type="button"
ref="dropdownButton"
- class="dropdown-menu-toggle wide js-label-select js-multiselect js-context-config-modal"
- data-toggle="dropdown"
:class="{ 'js-extra-options': showExtraOptions }"
:data-ability-name="abilityName"
:data-field-name="fieldName"
@@ -64,6 +61,9 @@ export default {
:data-labels="labelsPath"
:data-namespace-path="namespace"
:data-show-any="showExtraOptions"
+ type="button"
+ class="dropdown-menu-toggle wide js-label-select js-multiselect js-context-config-modal"
+ data-toggle="dropdown"
>
<span class="dropdown-toggle-text">
{{ dropdownToggleText }}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label.vue
index 3c400afdc1d..fe895136ccc 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_create_label.vue
@@ -19,9 +19,9 @@ export default {
<div class="dropdown-page-two dropdown-new-label">
<div class="dropdown-title">
<button
+ :aria-label="__('Go back')"
type="button"
class="dropdown-title-button dropdown-menu-back"
- :aria-label="__('Go back')"
>
<i
aria-hidden="true"
@@ -32,9 +32,9 @@ export default {
</button>
{{ headerTitle }}
<button
+ :aria-label="__('Close')"
type="button"
class="dropdown-title-button dropdown-menu-close"
- :aria-label="__('Close')"
>
<i
aria-hidden="true"
@@ -48,19 +48,19 @@ export default {
<div class="dropdown-labels-error js-label-error"></div>
<input
id="new_label_name"
+ :placeholder="__('Name new label')"
type="text"
class="default-dropdown-input"
- :placeholder="__('Name new label')"
/>
<div class="suggest-colors suggest-colors-dropdown">
<a
v-for="(color, index) in suggestedColors"
- href="#"
:key="index"
:data-color="color"
:style="{
backgroundColor: color,
}"
+ href="#"
>
&nbsp;
</a>
@@ -69,9 +69,9 @@ export default {
<div class="dropdown-label-color-preview js-dropdown-label-color-preview"></div>
<input
id="new_label_color"
+ :placeholder="__('Assign custom color like #FF0000')"
type="text"
class="default-dropdown-input"
- :placeholder="__('Assign custom color like #FF0000')"
/>
</div>
<div class="clearfix">
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer.vue
index 5f61e9fbe80..d64ad016f9b 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_footer.vue
@@ -34,9 +34,9 @@ export default {
</li>
<li>
<a
+ :href="labelsWebUrl"
data-is-link="true"
class="dropdown-external-link"
- :href="labelsWebUrl"
>
{{ manageLabelsTitle }}
</a>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue
index 7664acdf19c..e98b6392827 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_header.vue
@@ -6,9 +6,9 @@ export default {};
<div class="dropdown-title">
<span>{{ __('Assign labels') }}</span>
<button
+ :aria-label="__('Close')"
type="button"
class="dropdown-title-button dropdown-menu-close"
- :aria-label="__('Close')"
>
<i
aria-hidden="true"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input.vue
index ae633460c95..80d65a2a534 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_search_input.vue
@@ -5,10 +5,10 @@ export default {};
<template>
<div class="dropdown-input">
<input
+ :placeholder="__('Search')"
autocomplete="off"
class="dropdown-input-field"
type="search"
- :placeholder="__('Search')"
/>
<i
aria-hidden="true"
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue
index 88360b46f24..10e990f8a80 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value.vue
@@ -36,10 +36,10 @@ export default {
<template>
<div
- class="hide-collapsed value issuable-show-labels js-value"
:class="{
'has-labels':!isEmpty,
}"
+ class="hide-collapsed value issuable-show-labels js-value"
>
<span
v-if="isEmpty"
@@ -48,18 +48,18 @@ export default {
<slot>{{ __('None') }}</slot>
</span>
<a
- v-else
v-for="label in labels"
+ v-else
:key="label.id"
:href="labelFilterUrl(label)"
>
<span
v-tooltip
+ :style="labelStyle(label)"
+ :title="label.description"
class="badge color-label"
data-placement="bottom"
data-container="body"
- :style="labelStyle(label)"
- :title="label.description"
>
{{ label.title }}
</span>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
index 68fa2ab8d01..af297f3c408 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select/dropdown_value_collapsed.vue
@@ -37,10 +37,10 @@ export default {
<template>
<div
v-tooltip
+ :title="labelsList"
class="sidebar-collapsed-icon"
data-placement="left"
data-container="body"
- :title="labelsList"
@click="handleClick"
>
<i
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
index de6f8c32e74..ac2e99abe77 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
@@ -28,21 +28,21 @@ export default {
<template>
<button
+ v-tooltip
+ :title="tooltipLabel"
type="button"
class="btn btn-blank gutter-toggle btn-sidebar-action"
- @click="toggle"
- v-tooltip
data-container="body"
data-placement="left"
- :title="tooltipLabel"
+ @click="toggle"
>
<i
- aria-label="toggle collapse"
- class="fa"
:class="{
'fa-angle-double-right': !collapsed,
'fa-angle-double-left': collapsed
}"
+ aria-label="toggle collapse"
+ class="fa"
>
</i>
</button>
diff --git a/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue b/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
index 16304e4815d..4a5ffbe5d5a 100644
--- a/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
+++ b/app/assets/javascripts/vue_shared/components/skeleton_loading_container.vue
@@ -22,10 +22,10 @@
<template>
<div
- class="animation-container"
:class="{
'animation-container-small': small,
}"
+ class="animation-container"
>
<div
v-for="(css, index) in lineClasses"
diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
index 86f06c8d266..b1c2df54ef6 100644
--- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
+++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue
@@ -84,8 +84,8 @@ export default {
<template>
<div
- class="stacked-progress-bar"
:class="cssClass"
+ class="stacked-progress-bar"
>
<span
v-if="!totalCount"
@@ -96,30 +96,30 @@ export default {
<span
v-tooltip
v-if="successPercent"
- class="status-green"
- data-placement="bottom"
:title="successTooltip"
:style="successBarStyle"
+ class="status-green"
+ data-placement="bottom"
>
{{ successPercent }}%
</span>
<span
v-tooltip
v-if="neutralPercent"
- class="status-neutral"
- data-placement="bottom"
:title="neutralTooltip"
:style="neutralBarStyle"
+ class="status-neutral"
+ data-placement="bottom"
>
{{ neutralPercent }}%
</span>
<span
v-tooltip
v-if="failurePercent"
- class="status-red"
- data-placement="bottom"
:title="failureTooltip"
:style="failureBarStyle"
+ class="status-red"
+ data-placement="bottom"
>
{{ failurePercent }}%
</span>
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue
index 6f231619f26..2370e59d017 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue
@@ -153,8 +153,8 @@
class="page-item"
>
<a
- @click.prevent="changePage(item.title, item.disabled)"
class="page-link"
+ @click.prevent="changePage(item.title, item.disabled)"
>
{{ item.title }}
</a>
diff --git a/app/assets/javascripts/vue_shared/components/tabs/tab.vue b/app/assets/javascripts/vue_shared/components/tabs/tab.vue
index 9b2f46186ac..1c6011dcfd0 100644
--- a/app/assets/javascripts/vue_shared/components/tabs/tab.vue
+++ b/app/assets/javascripts/vue_shared/components/tabs/tab.vue
@@ -36,10 +36,10 @@ export default {
<template>
<div
- class="tab-pane"
:class="{
active: localActive
}"
+ class="tab-pane"
role="tabpanel"
>
<slot></slot>
diff --git a/app/assets/javascripts/vue_shared/components/toggle_button.vue b/app/assets/javascripts/vue_shared/components/toggle_button.vue
index 09031d3ffa1..a897300b62b 100644
--- a/app/assets/javascripts/vue_shared/components/toggle_button.vue
+++ b/app/assets/javascripts/vue_shared/components/toggle_button.vue
@@ -63,26 +63,26 @@
<label class="toggle-wrapper">
<input
v-if="name"
- type="hidden"
:name="name"
:value="value"
+ type="hidden"
/>
<button
- type="button"
- class="project-feature-toggle"
:aria-label="ariaLabel"
:class="{
'is-checked': value,
'is-disabled': disabledInput,
'is-loading': isLoading
}"
+ type="button"
+ class="project-feature-toggle"
@click="toggleFeature"
>
<loadingIcon class="loading-icon" />
<span class="toggle-icon">
<icon
- css-classes="toggle-icon-svg"
- :name="toggleIcon"/>
+ :name="toggleIcon"
+ css-classes="toggle-icon-svg"/>
</span>
</button>
</label>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
index cc9cc46bb4c..3a413c74410 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_image.vue
@@ -85,7 +85,6 @@ export default {
<template>
<img
v-tooltip
- class="avatar"
:class="{
lazy: lazy,
[avatarSizeClass]: true,
@@ -99,5 +98,7 @@ export default {
:data-container="tooltipContainer"
:data-placement="tooltipPlacement"
:title="tooltipText"
+ class="avatar"
+ data-boundary="window"
/>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
index 6955d164def..01c36fec41a 100644
--- a/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
+++ b/app/assets/javascripts/vue_shared/components/user_avatar/user_avatar_link.vue
@@ -84,8 +84,8 @@ export default {
<template>
<a
- class="user-avatar-link"
- :href="linkHref">
+ :href="linkHref"
+ class="user-avatar-link">
<user-avatar-image
:img-src="imgSrc"
:img-alt="imgAlt"
@@ -94,8 +94,8 @@ export default {
:tooltip-text="avatarTooltipText"
:tooltip-placement="tooltipPlacement"
/><span
- v-if="shouldShowUsername"
v-tooltip
+ v-if="shouldShowUsername"
:title="tooltipText"
:tooltip-placement="tooltipPlacement"
>{{ username }}</span>
diff --git a/app/assets/javascripts/vue_shared/models/assignee.js b/app/assets/javascripts/vue_shared/models/assignee.js
new file mode 100644
index 00000000000..4a29b0d0581
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/models/assignee.js
@@ -0,0 +1,13 @@
+export default class ListAssignee {
+ constructor(obj, defaultAvatar) {
+ this.id = obj.id;
+ this.name = obj.name;
+ this.username = obj.username;
+ this.avatar = obj.avatar_url || obj.avatar || defaultAvatar;
+ this.path = obj.path;
+ this.state = obj.state;
+ this.webUrl = obj.web_url || obj.webUrl;
+ }
+}
+
+window.ListAssignee = ListAssignee;
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index 79f580546c3..810ed5bb0a6 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -89,7 +89,12 @@ a {
color: $gl-link-color;
}
+hr {
+ overflow: hidden;
+}
+
.form-group.row .col-form-label {
+ padding-top: 0;
// Bootstrap 4 aligns labels to the left
// for horizontal forms
@include media-breakpoint-up(md) {
@@ -107,7 +112,7 @@ code {
background-color: $red-100;
border-radius: 3px;
- .code & {
+ .code > & {
background-color: inherit;
padding: unset;
}
@@ -118,10 +123,6 @@ code {
}
}
-.code {
- padding: 9.5px;
-}
-
table {
// Remove any table border lines
border-spacing: 0;
@@ -213,6 +214,10 @@ table {
border-bottom: 1px solid $well-inner-border;
}
}
+
+ .badge.badge-gray {
+ background-color: $well-expand-item;
+ }
}
.card {
@@ -233,6 +238,13 @@ table {
}
}
+.card-header {
+ h3.card-title,
+ h4.card-title {
+ margin-top: 0;
+ }
+}
+
.nav-tabs {
// Override bootstrap's default border
border-bottom: 0;
@@ -255,9 +267,29 @@ pre code {
.alert-danger {
background-color: $red-500;
border-color: $red-500;
+}
+
+.alert-warning,
+.alert-danger,
+.flash-notice {
+ border-radius: 0;
color: $white-light;
- h4 {
+ h4,
+ a,
+ .alert-link {
color: $white-light;
}
}
+
+input[type=color].form-control {
+ height: $input-height;
+}
+
+.toggle-sidebar-button {
+ .collapse-text,
+ .icon-angle-double-left,
+ .icon-angle-double-right {
+ color: $gl-text-color-secondary;
+ }
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 0115f542c88..523fcb05a87 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -121,10 +121,6 @@
&.btn-sm {
margin-left: $btn-sm-side-margin;
}
-
- &.btn-xs {
- margin-left: $btn-xs-side-margin;
- }
}
@mixin btn-svg {
@@ -150,10 +146,6 @@
line-height: 18px;
}
- &.btn-xs {
- padding: 2px 5px;
- }
-
&.btn-success,
&.btn-new,
&.btn-create,
@@ -497,6 +489,10 @@ fieldset[disabled] .btn,
}
}
+[readonly] {
+ cursor: default;
+}
+
.btn-no-padding {
padding: 0;
}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 996e5c1512d..e5197e27b82 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -305,14 +305,6 @@ img.emoji {
margin-bottom: 10px;
}
-.btn-sign-in {
- text-shadow: none;
-
- @include media-breakpoint-up(sm) {
- margin-top: 8px;
- }
-}
-
.side-filters {
fieldset {
margin-bottom: 15px;
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index f77ec4b6a2c..f060254777c 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -400,3 +400,51 @@ span.idiff {
color: $common-gray-light;
border: 1px solid $common-gray-light;
}
+
+.preview-container {
+ height: 100%;
+ overflow: auto;
+
+ .file-container {
+ background-color: $gray-darker;
+ display: flex;
+ height: 100%;
+ align-items: center;
+ justify-content: center;
+
+ text-align: center;
+
+ .file-content {
+ padding: $gl-padding;
+ max-width: 100%;
+ max-height: 100%;
+
+ img {
+ max-width: 90%;
+ max-height: 70vh;
+ }
+
+ .is-zoomable {
+ cursor: pointer;
+ cursor: zoom-in;
+
+ &.is-zoomed {
+ cursor: pointer;
+ cursor: zoom-out;
+ max-width: none;
+ max-height: none;
+ margin-right: $gl-padding;
+ }
+ }
+ }
+
+ .file-info {
+ font-size: $label-font-size;
+ color: $diff-image-info-color;
+ }
+ }
+
+ .md-previewer {
+ padding: $gl-padding;
+ }
+}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index c76ea532912..03520f42997 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -170,7 +170,7 @@ label {
}
.form-control::-webkit-input-placeholder {
- color: $gl-text-color-secondary;
+ color: $placeholder-text-color;
}
.input-group {
diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss
index 14dd3879bdc..b40d02f381a 100644
--- a/app/assets/stylesheets/framework/gitlab_theme.scss
+++ b/app/assets/stylesheets/framework/gitlab_theme.scss
@@ -3,26 +3,26 @@
*/
@mixin gitlab-theme(
- $color-100,
- $color-200,
- $color-500,
- $color-700,
- $color-800,
- $color-900,
+ $location-badge-color,
+ $search-and-nav-links,
+ $active-tab-border,
+ $border-and-box-shadow,
+ $sidebar-text,
+ $nav-svg-color,
$color-alternate
) {
// Header
.navbar-gitlab {
- background-color: $color-900;
+ background-color: $nav-svg-color;
.navbar-collapse {
- color: $color-200;
+ color: $search-and-nav-links;
}
.container-fluid {
.navbar-toggler {
- border-left: 1px solid lighten($color-700, 10%);
+ border-left: 1px solid lighten($border-and-box-shadow, 10%);
}
}
@@ -31,40 +31,40 @@
> li {
> a:hover,
> a:focus {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
}
&.active > a,
&.dropdown.show > a {
- color: $color-900;
+ color: $nav-svg-color;
background-color: $color-alternate;
}
&.line-separator {
- border-left: 1px solid rgba($color-200, 0.2);
+ border-left: 1px solid rgba($search-and-nav-links, 0.2);
}
}
}
.navbar-sub-nav {
- color: $color-200;
+ color: $search-and-nav-links;
}
.nav {
> li {
- color: $color-200;
+ color: $search-and-nav-links;
> a {
&.header-user-dropdown-toggle {
.header-user-avatar {
- border-color: $color-200;
+ border-color: $search-and-nav-links;
}
}
&:hover,
&:focus {
@include media-breakpoint-up(sm) {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
}
svg {
@@ -75,12 +75,12 @@
&.active > a,
&.dropdown.show > a {
- color: $color-900;
+ color: $nav-svg-color;
background-color: $color-alternate;
&:hover {
svg {
- fill: $color-900;
+ fill: $nav-svg-color;
}
}
}
@@ -88,7 +88,7 @@
.impersonated-user,
.impersonated-user:hover {
svg {
- fill: $color-900;
+ fill: $nav-svg-color;
}
}
}
@@ -99,34 +99,34 @@
> a {
&:hover,
&:focus {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
}
}
}
.search {
form {
- background-color: rgba($color-200, 0.2);
+ background-color: rgba($search-and-nav-links, 0.2);
&:hover {
- background-color: rgba($color-200, 0.3);
+ background-color: rgba($search-and-nav-links, 0.3);
}
}
.location-badge {
- color: $color-100;
- background-color: rgba($color-200, 0.1);
- border-right: 1px solid $color-800;
+ color: $location-badge-color;
+ background-color: rgba($search-and-nav-links, 0.1);
+ border-right: 1px solid $sidebar-text;
}
.search-input::placeholder {
- color: rgba($color-200, 0.8);
+ color: rgba($search-and-nav-links, 0.8);
}
.search-input-wrap {
.search-icon,
.clear-icon {
- fill: rgba($color-200, 0.8);
+ fill: rgba($search-and-nav-links, 0.8);
}
}
@@ -141,38 +141,34 @@
.search-input-wrap {
.search-icon {
- fill: rgba($color-200, 0.8);
+ fill: rgba($search-and-nav-links, 0.8);
}
}
}
}
- .btn-sign-in {
- background-color: $color-100;
- color: $color-900;
- }
// Sidebar
.nav-sidebar li.active {
- box-shadow: inset 4px 0 0 $color-700;
+ box-shadow: inset 4px 0 0 $border-and-box-shadow;
> a {
- color: $color-800;
+ color: $sidebar-text;
}
svg {
- fill: $color-800;
+ fill: $sidebar-text;
}
}
.sidebar-top-level-items > li.active .badge.badge-pill {
- color: $color-800;
+ color: $sidebar-text;
}
.nav-links li {
&.active a,
a.active {
- border-bottom: 2px solid $color-500;
+ border-bottom: 2px solid $active-tab-border;
.badge.badge-pill {
font-weight: $gl-font-weight-bold;
@@ -181,27 +177,27 @@
}
.branch-header-title {
- color: $color-700;
+ color: $border-and-box-shadow;
}
.ide-file-list .file.file-active {
- color: $color-700;
+ color: $border-and-box-shadow;
}
.ide-sidebar-link {
&.active {
- color: $color-700;
- box-shadow: inset 3px 0 $color-700;
+ color: $border-and-box-shadow;
+ box-shadow: inset 3px 0 $border-and-box-shadow;
&.is-right {
- box-shadow: inset -3px 0 $color-700;
+ box-shadow: inset -3px 0 $border-and-box-shadow;
}
}
}
}
body {
- &.ui_indigo {
+ &.ui-indigo {
@include gitlab-theme(
$indigo-100,
$indigo-200,
@@ -213,19 +209,19 @@ body {
);
}
- &.ui_dark {
+ &.ui-light-indigo {
@include gitlab-theme(
- $theme-gray-100,
- $theme-gray-200,
- $theme-gray-500,
- $theme-gray-700,
- $theme-gray-800,
- $theme-gray-900,
+ $indigo-100,
+ $indigo-200,
+ $indigo-500,
+ $indigo-500,
+ $indigo-700,
+ $indigo-700,
$white-light
);
}
- &.ui_blue {
+ &.ui-blue {
@include gitlab-theme(
$theme-blue-100,
$theme-blue-200,
@@ -237,7 +233,19 @@ body {
);
}
- &.ui_green {
+ &.ui-light-blue {
+ @include gitlab-theme(
+ $theme-light-blue-100,
+ $theme-light-blue-200,
+ $theme-light-blue-500,
+ $theme-light-blue-500,
+ $theme-light-blue-700,
+ $theme-light-blue-700,
+ $white-light
+ );
+ }
+
+ &.ui-green {
@include gitlab-theme(
$theme-green-100,
$theme-green-200,
@@ -249,7 +257,55 @@ body {
);
}
- &.ui_light {
+ &.ui-light-green {
+ @include gitlab-theme(
+ $theme-green-100,
+ $theme-green-200,
+ $theme-green-500,
+ $theme-green-500,
+ $theme-light-green-700,
+ $theme-light-green-700,
+ $white-light
+ );
+ }
+
+ &.ui-red {
+ @include gitlab-theme(
+ $theme-red-100,
+ $theme-red-200,
+ $theme-red-500,
+ $theme-red-700,
+ $theme-red-800,
+ $theme-red-900,
+ $white-light
+ );
+ }
+
+ &.ui-light-red {
+ @include gitlab-theme(
+ $theme-light-red-100,
+ $theme-light-red-200,
+ $theme-light-red-500,
+ $theme-light-red-500,
+ $theme-light-red-700,
+ $theme-light-red-700,
+ $white-light
+ );
+ }
+
+ &.ui-dark {
+ @include gitlab-theme(
+ $theme-gray-100,
+ $theme-gray-200,
+ $theme-gray-500,
+ $theme-gray-700,
+ $theme-gray-800,
+ $theme-gray-900,
+ $white-light
+ );
+ }
+
+ &.ui-light {
@include gitlab-theme(
$theme-gray-900,
$theme-gray-700,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 094134b63b0..db59c91e375 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -139,6 +139,8 @@
}
.nav {
+ flex-wrap: nowrap;
+
> li:not(.d-none) a {
@include media-breakpoint-down(xs) {
margin-left: 0;
@@ -158,11 +160,12 @@
}
.navbar-toggler {
+ position: relative;
right: -10px;
border-radius: 0;
min-width: 45px;
padding: 0;
- margin-right: -7px;
+ margin: $gl-padding-8 -7px $gl-padding-8 0;
font-size: 14px;
text-align: center;
color: currentColor;
@@ -186,6 +189,7 @@
display: -webkit-flex;
display: flex;
padding-right: 10px;
+ flex-direction: row;
}
li {
@@ -264,6 +268,8 @@
.navbar-sub-nav,
.navbar-nav {
+ align-items: center;
+
> li {
> a:hover,
> a:focus {
@@ -290,6 +296,10 @@
margin: 8px;
}
}
+
+ .dropdown-menu {
+ position: absolute;
+ }
}
.navbar-sub-nav {
@@ -437,12 +447,18 @@
}
.btn-sign-in {
- margin-top: 3px;
+ background-color: $indigo-100;
+ color: $indigo-900;
font-weight: $gl-font-weight-bold;
+ line-height: 18px;
&:hover {
background-color: $white-light;
}
+
+ @include media-breakpoint-down(xs) {
+ margin-top: $gl-padding-4;
+ }
}
.navbar-nav {
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index b893151e4fe..7290a174668 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -61,10 +61,6 @@
padding-top: 0;
line-height: 19px;
- &.btn.btn-xs {
- padding: 2px 5px;
- }
-
&:focus {
margin-top: -10px;
padding-top: 10px;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index d76cf8f8182..0b645eb811b 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -186,6 +186,7 @@
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
display: flex;
+ flex-wrap: nowrap;
&::-webkit-scrollbar {
display: none;
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index 50a1b1c446d..61d02511ff4 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -4,3 +4,11 @@
text-decoration: none;
}
}
+
+.page-item {
+ &.active {
+ .page-link {
+ z-index: 3;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 847fc8c0792..2d9e9e6a67d 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -197,7 +197,7 @@
flex-flow: row wrap;
.nav-controls {
- $controls-margin: $btn-xs-side-margin - 2px;
+ $controls-margin: $btn-margin-5 - 2px;
flex: 0 0 100%;
&.controls-flex {
@@ -230,6 +230,8 @@
}
.scrolling-tabs-container {
+ position: relative;
+
.merge-request-tabs-container & {
overflow: hidden;
}
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 10c23f6c407..339388392df 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -39,6 +39,11 @@ table {
&.wide {
width: 55%;
}
+
+ &.table-th-transparent {
+ background: none;
+ color: $gl-text-color-secondary;
+ }
}
td {
@@ -46,9 +51,91 @@ table {
}
}
}
+
+ &.responsive-table {
+ @include media-breakpoint-down(sm) {
+ thead {
+ display: none;
+ }
+
+ &,
+ tbody,
+ td {
+ display: block;
+ }
+
+ td {
+ color: $gl-text-color-secondary;
+ }
+
+ tbody td.responsive-table-cell {
+ padding: $gl-padding 0;
+ width: 100%;
+ display: flex;
+ text-align: right;
+ align-items: center;
+ justify-content: space-between;
+
+ &[data-column]::before {
+ content: attr(data-column);
+ display: block;
+ text-align: left;
+ padding-right: $gl-padding;
+ color: $gl-text-color-secondary;
+ }
+
+ &:not([data-column]) {
+ flex-direction: row-reverse;
+ }
+ }
+
+ tr.responsive-table-border-start,
+ tr.responsive-table-border-end {
+ display: block;
+ border: solid $gl-text-color-quaternary;
+ padding-left: 0;
+ padding-right: 0;
+
+ > td {
+ border-color: $gl-text-color-quaternary;
+
+ &,
+ &:last-child {
+ padding-left: $gl-padding;
+ padding-right: $gl-padding;
+ }
+ }
+ }
+
+ tr.responsive-table-border-start {
+ border-width: 1px 1px 0;
+ border-radius: $border-radius-default $border-radius-default 0 0;
+ padding-top: 0;
+ padding-bottom: 0;
+
+ > td:first-child {
+ border-top: 0; // always have the <table> top border
+ }
+
+ > td:last-child {
+ border-bottom: 1px solid $gl-text-color-quaternary;
+ }
+ }
+
+ tr.responsive-table-border-end {
+ border-width: 0 1px 1px;
+ border-radius: 0 0 $border-radius-default $border-radius-default;
+ margin-bottom: 2 * $gl-padding;
+
+ > :last-child {
+ border-bottom: 0;
+ }
+ }
+ }
+ }
}
-.responsive-table {
+.responsive-table:not(table) {
@include media-breakpoint-down(sm) {
th {
width: 100%;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index 75c11590547..dfb145debe7 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -4,7 +4,7 @@
padding: 0;
&::before {
- @include notes-media('max', map-get($grid-breakpoints, xs)) {
+ @include notes-media('max', map-get($grid-breakpoints, sm)) {
background: none;
}
}
@@ -34,7 +34,7 @@
.timeline-entry-inner {
position: relative;
- @include notes-media('max', map-get($grid-breakpoints, xs)) {
+ @include notes-media('max', map-get($grid-breakpoints, sm)) {
.timeline-icon {
display: none;
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 946223cfff0..0826bfd0035 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -99,7 +99,7 @@ $theme-gray-200: #dfdfdf;
$theme-gray-300: #cccccc;
$theme-gray-400: #bababa;
$theme-gray-500: #a7a7a7;
-$theme-gray-600: #949494;
+$theme-gray-600: #919191;
$theme-gray-700: #707070;
$theme-gray-800: #4f4f4f;
$theme-gray-900: #2e2e2e;
@@ -117,6 +117,15 @@ $theme-blue-800: #25496e;
$theme-blue-900: #1a3652;
$theme-blue-950: #0f2235;
+$theme-light-blue-50: #f2f7fc;
+$theme-light-blue-100: #ebf1f7;
+$theme-light-blue-200: #c9dcf2;
+$theme-light-blue-300: #83abd4;
+$theme-light-blue-400: #4d86bf;
+$theme-light-blue-500: #367cc2;
+$theme-light-blue-600: #3771ab;
+$theme-light-blue-700: #2261a1;
+
$theme-green-50: #f2faf6;
$theme-green-100: #e4f3ea;
$theme-green-200: #c0dfcd;
@@ -129,6 +138,29 @@ $theme-green-800: #145d33;
$theme-green-900: #0d4524;
$theme-green-950: #072d16;
+$theme-light-green-700: #156b39;
+
+$theme-red-50: #fcf4f2;
+$theme-red-100: #fae9e6;
+$theme-red-200: #ebcac5;
+$theme-red-300: #d99b91;
+$theme-red-400: #b0655a;
+$theme-red-500: #ad4a3b;
+$theme-red-600: #9e4133;
+$theme-red-700: #912f20;
+$theme-red-800: #78291d;
+$theme-red-900: #691a16;
+$theme-red-950: #36140f;
+
+$theme-light-red-50: #fff6f5;
+$theme-light-red-100: #fae2de;
+$theme-light-red-200: #f7d5d0;
+$theme-light-red-300: #d9796a;
+$theme-light-red-400: #cf604e;
+$theme-light-red-500: #c24b38;
+$theme-light-red-600: #b03927;
+$theme-light-red-700: #a62e21;
+
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
$almost-black: #242424;
@@ -142,11 +174,6 @@ $border-gray-normal-dashed: darken($gray-normal, $darken-border-dashed-factor);
$border-gray-dark: darken($white-normal, $darken-border-factor);
/*
- * Override Bootstrap 4 variables
- */
-$secondary: $gray-light;
-
-/*
* UI elements
*/
$border-color: #e5e5e5;
@@ -164,7 +191,7 @@ $gl-font-weight-normal: 400;
$gl-font-weight-bold: 600;
$gl-text-color: #2e2e2e;
$gl-text-color-secondary: #707070;
-$gl-text-color-tertiary: #949494;
+$gl-text-color-tertiary: #919191;
$gl-text-color-quaternary: #d6d6d6;
$gl-text-color-inverted: rgba(255, 255, 255, 1);
$gl-text-color-secondary-inverted: rgba(255, 255, 255, 0.85);
@@ -251,7 +278,7 @@ $active-item-blue: $blue-500;
$layout-link-gray: #7e7c7c;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
-$btn-xs-side-margin: 5px;
+$btn-margin-5: 5px;
$issue-status-expired: $orange-500;
$issuable-sidebar-color: $gl-text-color-secondary;
$sidebar-block-hover-color: #ebebeb;
@@ -408,6 +435,22 @@ $badge-bg: rgba(0, 0, 0, 0.07);
$badge-color: $gl-text-color-secondary;
/*
+* Pagination
+*/
+$pagination-padding-y: 6px;
+$pagination-padding-x: 16px;
+$pagination-line-height: 20px;
+$pagination-border-color: $border-color;
+$pagination-active-bg: $blue-600;
+$pagination-active-border-color: $blue-600;
+$pagination-hover-bg: $blue-50;
+$pagination-hover-border-color: $border-color;
+$pagination-hover-color: $gl-text-color;
+$pagination-disabled-color: #cdcdcd;
+$pagination-disabled-bg: $gray-light;
+$pagination-disabled-border-color: $border-color;
+
+/*
* Status icons
*/
$status-icon-size: 22px;
@@ -776,3 +819,16 @@ $modal-body-height: 134px;
Prometheus
*/
$prometheus-table-row-highlight-color: $theme-gray-100;
+
+$priority-label-empty-state-width: 114px;
+
+/*
+ * Override Bootstrap 4 variables
+ */
+
+$secondary: $gray-light;
+$input-disabled-bg: $gray-light;
+$input-border-color: $theme-gray-200;
+$input-color: $gl-text-color;
+$font-family-sans-serif: $regular_font;
+$font-family-monospace: $monospace_font;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index b2416a3d5bc..7c1d1626f1c 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -80,6 +80,7 @@
overflow-x: scroll;
white-space: nowrap;
min-height: 200px;
+ display: flex;
@include media-breakpoint-only(sm) {
height: calc(100vh - #{$issue-board-list-difference-sm});
@@ -110,17 +111,15 @@
.board {
display: inline-block;
- width: calc(85vw - 15px);
+ flex: 1;
+ min-width: 300px;
+ max-width: 400px;
height: 100%;
padding-right: ($gl-padding / 2);
padding-left: ($gl-padding / 2);
white-space: normal;
vertical-align: top;
- @include media-breakpoint-up(sm) {
- width: 400px;
- }
-
&.is-expandable {
.board-header {
cursor: pointer;
@@ -128,6 +127,8 @@
}
&.is-collapsed {
+ flex: none;
+ min-width: 0;
width: 50px;
.board-header {
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 9213ccd4cdf..f030189af06 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -12,26 +12,22 @@
@keyframes blinking-dots {
0% {
background-color: rgba($white-light, 1);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
- 24px 0 0 0 rgba($white-light, 0.2);
+ box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2);
}
25% {
background-color: rgba($white-light, 0.4);
- box-shadow: 12px 0 0 0 rgba($white-light, 2),
- 24px 0 0 0 rgba($white-light, 0.2);
+ box-shadow: 12px 0 0 0 rgba($white-light, 2), 24px 0 0 0 rgba($white-light, 0.2);
}
75% {
background-color: rgba($white-light, 0.4);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
- 24px 0 0 0 rgba($white-light, 1);
+ box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 1);
}
100% {
background-color: rgba($white-light, 1);
- box-shadow: 12px 0 0 0 rgba($white-light, 0.2),
- 24px 0 0 0 rgba($white-light, 0.2);
+ box-shadow: 12px 0 0 0 rgba($white-light, 0.2), 24px 0 0 0 rgba($white-light, 0.2);
}
}
@@ -71,6 +67,10 @@
.bash {
display: block;
}
+
+ &.build-trace-rounded {
+ border-radius: $border-radius-base;
+ }
}
.top-bar {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index f06c9dcdf8c..fbc97ec0c95 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -189,8 +189,22 @@
img {
border: 1px solid $white-light;
- background-image: linear-gradient(45deg, $border-color 25%, transparent 25%, transparent 75%, $border-color 75%, $border-color 100%),
- linear-gradient(45deg, $border-color 25%, transparent 25%, transparent 75%, $border-color 75%, $border-color 100%);
+ background-image: linear-gradient(
+ 45deg,
+ $border-color 25%,
+ transparent 25%,
+ transparent 75%,
+ $border-color 75%,
+ $border-color 100%
+ ),
+ linear-gradient(
+ 45deg,
+ $border-color 25%,
+ transparent 25%,
+ transparent 75%,
+ $border-color 75%,
+ $border-color 100%
+ );
background-size: 10px 10px;
background-position: 0 0, 5px 5px;
max-width: 100%;
@@ -395,6 +409,69 @@
.line_content {
white-space: pre-wrap;
}
+
+ .diff-file-container {
+ .frame.deleted {
+ border: 0;
+ background-color: inherit;
+
+ .image_file img {
+ border: 1px solid $deleted;
+ }
+ }
+
+ .frame.added {
+ border: 0;
+ background-color: inherit;
+
+ .image_file img {
+ border: 1px solid $added;
+ }
+ }
+
+ .swipe.view,
+ .onion-skin.view {
+ .swipe-wrap {
+ top: 0;
+ right: 0;
+ }
+
+ .frame.deleted {
+ top: 0;
+ right: 0;
+ }
+
+ .swipe-bar {
+ top: 0;
+
+ .top-handle {
+ top: -14px;
+ left: -7px;
+ }
+
+ .bottom-handle {
+ bottom: -14px;
+ left: -7px;
+ }
+ }
+
+ .file-container {
+ display: inline-block;
+
+ .file-content {
+ padding: 0;
+
+ img {
+ max-width: none;
+ }
+ }
+ }
+ }
+
+ .onion-skin.view .controls {
+ bottom: -25px;
+ }
+ }
}
.file-content .diff-file {
@@ -536,7 +613,7 @@
margin-right: 0;
border-color: $white-light;
cursor: pointer;
- transition: all .1s ease-out;
+ transition: all 0.1s ease-out;
@for $i from 1 through 4 {
&:nth-child(#{$i}) {
@@ -563,7 +640,7 @@
height: 24px;
border-radius: 50%;
padding: 0;
- transition: transform .1s ease-out;
+ transition: transform 0.1s ease-out;
z-index: 100;
.collapse-icon {
@@ -708,11 +785,35 @@
width: 100%;
height: 10px;
background-color: $white-light;
- background-image: linear-gradient(45deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%),
- linear-gradient(225deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%),
- linear-gradient(135deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%),
- linear-gradient(-45deg, transparent, transparent 73%, $diff-jagged-border-gradient-color 75%, $white-light 80%);
- background-position: 5px 5px,0 5px,0 5px,5px 5px;
+ background-image: linear-gradient(
+ 45deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%
+ ),
+ linear-gradient(
+ 225deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%
+ ),
+ linear-gradient(
+ 135deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%
+ ),
+ linear-gradient(
+ -45deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%
+ );
+ background-position: 5px 5px, 0 5px, 0 5px, 5px 5px;
background-size: 10px 10px;
background-repeat: repeat;
}
@@ -750,11 +851,16 @@
.frame.click-to-comment {
position: relative;
cursor: image-url('illustrations/image_comment_light_cursor.svg')
- $image-comment-cursor-left-offset $image-comment-cursor-top-offset, auto;
+ $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
+ auto;
// Retina cursor
- cursor: -webkit-image-set(image-url('illustrations/image_comment_light_cursor.svg') 1x, image-url('illustrations/image_comment_light_cursor@2x.svg') 2x)
- $image-comment-cursor-left-offset $image-comment-cursor-top-offset, auto;
+ cursor: -webkit-image-set(
+ image-url('illustrations/image_comment_light_cursor.svg') 1x,
+ image-url('illustrations/image_comment_light_cursor@2x.svg') 2x
+ )
+ $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
+ auto;
.comment-indicator {
position: absolute;
@@ -840,7 +946,7 @@
.diff-notes-collapse,
.note,
- .discussion-reply-holder, {
+ .discussion-reply-holder {
display: none;
}
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index c2b42e02eee..05bf5596fb3 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -425,7 +425,7 @@
margin-left: 5px;
> .btn {
- margin-right: $btn-xs-side-margin;
+ margin-right: $btn-margin-5;
}
}
}
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index e178371d21f..6882b4adb15 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -57,67 +57,6 @@
border-bottom-left-radius: $border-radius-base;
}
-.label-row {
- .label-name {
- display: inline-block;
- margin-bottom: 10px;
-
- @include media-breakpoint-up(sm) {
- width: 200px;
- margin-left: $gl-padding * 2;
- margin-bottom: 0;
- }
-
- .badge {
- overflow: hidden;
- text-overflow: ellipsis;
- max-width: 100%;
- }
- }
-
- .label-type {
- display: block;
- margin-bottom: 10px;
- margin-left: 50px;
-
- @include media-breakpoint-up(sm) {
- display: inline-block;
- width: 100px;
- margin-left: 10px;
- margin-bottom: 0;
- vertical-align: top;
- }
- }
-
- .label-description {
- display: block;
- margin-bottom: 10px;
-
- .description-text {
- margin-bottom: $gl-padding;
- }
-
- a {
- color: $blue-600;
- }
-
- @include media-breakpoint-up(sm) {
- display: inline-block;
- max-width: 50%;
- margin-left: 10px;
- margin-bottom: 0;
- vertical-align: top;
- }
- }
-
- .badge {
- padding: 4px $grid-size;
- font-size: $label-font-size;
- position: relative;
- top: ($grid-size / 2);
- }
-}
-
.color-label {
padding: 0 $grid-size;
line-height: 16px;
@@ -133,26 +72,29 @@
}
.manage-labels-list {
- @media(min-width: map-get($grid-breakpoints, md)) {
- &.content-list li {
- padding: $gl-padding 0;
- }
- }
-
> li:not(.empty-message):not(.is-not-draggable) {
background-color: $white-light;
- cursor: move;
- cursor: -webkit-grab;
- cursor: -moz-grab;
-
- &:active {
- cursor: -webkit-grabbing;
- cursor: -moz-grabbing;
- }
+ margin-bottom: 5px;
+ display: flex;
+ justify-content: space-between;
+ padding: $gl-padding;
+ border-radius: $border-radius-default;
&.sortable-ghost {
opacity: 0.3;
}
+
+ .prioritized-labels & {
+ box-shadow: 0 1px 2px $issue-boards-card-shadow;
+ cursor: move;
+ cursor: -webkit-grab;
+ cursor: -moz-grab;
+
+ &:active {
+ cursor: -webkit-grabbing;
+ cursor: -moz-grabbing;
+ }
+ }
}
.btn-action {
@@ -171,28 +113,11 @@
}
}
- .dropdown {
- @include media-breakpoint-up(sm) {
- float: right;
- }
- }
-
- @include media-breakpoint-down(xs) {
- .dropdown-menu {
- min-width: 100%;
- }
+ .color-label {
+ padding: $gl-padding-4 $grid-size;
}
}
-.draggable-handler {
- display: inline-block;
- vertical-align: top;
- margin: 5px 0;
- opacity: 0;
- transition: opacity .3s;
- color: $gray-darkest;
-}
-
.prioritized-labels {
margin-bottom: 30px;
@@ -215,22 +140,6 @@
}
}
-.toggle-priority {
- display: inline-block;
- vertical-align: top;
-
- button {
- border-color: transparent;
- padding: 5px 8px;
- vertical-align: top;
- font-size: 14px;
-
- &:hover {
- border-color: transparent;
- }
- }
-}
-
.filtered-labels {
font-size: 0;
padding: 12px 16px;
@@ -284,10 +193,8 @@
}
.label-subscribe-button {
- @media(min-width: map-get($grid-breakpoints, md)) {
- min-width: 105px;
- margin-left: $gl-padding;
- }
+ width: 105px;
+ font-weight: 200;
.label-subscribe-button-icon {
&[disabled] {
@@ -324,3 +231,95 @@
font-size: $label-font-size;
}
}
+
+.labels-container {
+ background-color: $gray-light;
+ border-radius: $border-radius-default;
+ padding: $gl-padding $gl-padding-8;
+}
+
+.label-actions-list {
+ list-style: none;
+ flex-shrink: 0;
+ padding: 0;
+}
+
+.label-badge {
+ color: $theme-gray-900;
+ font-weight: $gl-font-weight-normal;
+ padding: $gl-padding-4 $gl-padding-8;
+ border-radius: $border-radius-default;
+ font-size: $label-font-size;
+}
+
+.label-badge-blue {
+ background-color: $theme-blue-100;
+}
+
+.label-badge-gray {
+ background-color: $theme-gray-100;
+}
+
+.label-links {
+ list-style: none;
+ padding: 0;
+ white-space: nowrap;
+}
+
+.label-link-item {
+ padding: 0;
+}
+
+.label-list-item {
+ .content-list &::before,
+ .content-list &::after {
+ content: none;
+ }
+
+ .label-name {
+ width: 150px;
+ flex-shrink: 0;
+
+ .label {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ max-width: 100%;
+ }
+ }
+
+ .label-description {
+ flex-grow: 1;
+
+ a {
+ color: $blue-600;
+ }
+ }
+
+ .label {
+ padding: 4px $grid-size;
+ font-size: $label-font-size;
+ position: relative;
+ top: $gl-padding-4;
+ }
+
+ .label-action {
+ color: $theme-gray-800;
+ cursor: pointer;
+
+ svg {
+ fill: $theme-gray-800;
+ }
+
+ &:hover {
+ color: $blue-600;
+
+ svg {
+ fill: $blue-600;
+ }
+ }
+ }
+}
+
+.priority-labels-empty-state .svg-content img {
+ max-width: $priority-label-empty-state-width;
+}
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 9914555d309..5fdb2b4a90a 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -121,10 +121,6 @@
background: transparent;
border: 0;
outline: 0;
-
- @include media-breakpoint-up(sm) {
- right: 160px;
- }
}
.flex-project-members-panel {
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 9eceb3e9a33..99fe4a578be 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -51,12 +51,6 @@
opacity: 0.3;
}
- &.btn-xs {
- line-height: 1;
- padding: 5px 10px;
- margin-top: 1px;
- }
-
&.dropdown-toggle {
.fa {
color: inherit;
@@ -678,6 +672,7 @@
.merge-request-tabs {
display: flex;
+ flex-wrap: nowrap;
margin-bottom: 0;
padding: 0;
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 3b037d066dc..3849a04db5d 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -129,7 +129,7 @@
.icon svg {
position: relative;
top: 2px;
- margin-right: $btn-xs-side-margin;
+ margin-right: $btn-margin-5;
width: $gl-font-size;
height: $gl-font-size;
fill: $orange-600;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index f85f66b9c0b..30428fd198d 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -321,18 +321,17 @@
}
.build-failures {
+ th {
+ border-top: 0;
+ }
+
.build-state {
padding: 20px 2px;
.build-name {
- float: right;
font-weight: $gl-font-weight-normal;
}
- .ci-status-icon-failed svg {
- vertical-align: middle;
- }
-
.stage {
color: $gl-text-color-secondary;
font-weight: $gl-font-weight-normal;
@@ -344,6 +343,81 @@
border: 0;
line-height: initial;
}
+
+ .build-trace-row td {
+ border-top: 0;
+ border-bottom-width: 1px;
+ border-bottom-style: solid;
+ padding-top: 0;
+ }
+
+ .build-trace {
+ width: 100%;
+ text-align: left;
+ margin-top: $gl-padding;
+ }
+
+ .build-name {
+ width: 196px;
+
+ a {
+ font-weight: $gl-font-weight-bold;
+ color: $gl-text-color;
+ text-decoration: none;
+
+ &:focus,
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .build-actions {
+ width: 70px;
+ text-align: right;
+ }
+
+ .build-stage {
+ width: 140px;
+ }
+
+ .ci-status-icon-failed {
+ padding: 10px 0 10px 12px;
+ width: 12px + 24px; // padding-left + svg width
+ }
+
+ .build-icon svg {
+ width: 24px;
+ height: 24px;
+ vertical-align: middle;
+ }
+
+ .build-state,
+ .build-trace-row {
+ > td:last-child {
+ padding-right: 0;
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+ td:empty {
+ display: none;
+ }
+
+ .ci-table {
+ margin-top: 2 * $gl-padding;
+ }
+
+ .build-trace-container {
+ padding-top: $gl-padding;
+ padding-bottom: $gl-padding;
+ }
+
+ .build-trace {
+ margin-bottom: 0;
+ margin-top: 0;
+ }
+ }
}
.pipeline-tab-content {
@@ -929,7 +1003,7 @@ button.mini-pipeline-graph-dropdown-toggle {
&.dropdown-menu {
transform: translate(-80%, 0);
- @media(min-width: map-get($grid-breakpoints, md)) {
+ @media (min-width: map-get($grid-breakpoints, md)) {
transform: translate(-50%, 0);
right: auto;
left: 50%;
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index 68d40b56133..babe81cb0f7 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -1,25 +1,3 @@
-@mixin application-theme-preview($color-1, $color-2, $color-3, $color-4) {
- .one {
- background-color: $color-1;
- border-top-left-radius: $border-radius-default;
- }
-
- .two {
- background-color: $color-2;
- border-top-right-radius: $border-radius-default;
- }
-
- .three {
- background-color: $color-3;
- border-bottom-left-radius: $border-radius-default;
- }
-
- .four {
- background-color: $color-4;
- border-bottom-right-radius: $border-radius-default;
- }
-}
-
.multi-file-editor-options {
label {
margin-right: 20px;
@@ -38,44 +16,61 @@
.application-theme {
label {
- margin-right: 20px;
+ margin: 0 $gl-padding $gl-padding 0;
text-align: center;
}
.preview {
font-size: 0;
- margin-bottom: 10px;
+ height: 48px;
+ border-radius: 4px;
+ min-width: 135px;
+ margin-bottom: $gl-padding-8;
+
+ &.ui-indigo {
+ background-color: $indigo-900;
+ }
+
+ &.ui-light-indigo {
+ background-color: $indigo-700;
+ }
- &.indigo {
- @include application-theme-preview($indigo-900, $indigo-700, $indigo-800, $indigo-500);
+ &.ui-blue {
+ background-color: $theme-blue-900;
}
- &.dark {
- @include application-theme-preview($theme-gray-900, $theme-gray-700, $theme-gray-800, $theme-gray-600);
+ &.ui-light-blue {
+ background-color: $theme-light-blue-700;
}
- &.light {
- @include application-theme-preview($theme-gray-600, $theme-gray-200, $theme-gray-400, $theme-gray-100);
+ &.ui-green {
+ background-color: $theme-green-900;
}
- &.blue {
- @include application-theme-preview($theme-blue-900, $theme-blue-700, $theme-blue-800, $theme-blue-500);
+ &.ui-light-green {
+ background-color: $theme-light-green-700;
}
- &.green {
- @include application-theme-preview($theme-green-900, $theme-green-700, $theme-green-800, $theme-green-500);
+ &.ui-red {
+ background-color: $theme-red-900;
+ }
+
+ &.ui-light-red {
+ background-color: $theme-light-red-700;
+ }
+
+ &.ui-dark {
+ background-color: $theme-gray-900;
+ }
+
+ &.ui-light {
+ background-color: $theme-gray-200;
}
}
.preview-row {
display: block;
}
-
- .quadrant {
- display: inline-block;
- height: 50px;
- width: 80px;
- }
}
.syntax-theme {
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 22964163e95..caafda5fb05 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -354,12 +354,6 @@
min-width: 200px;
}
-.deploy-keys {
- .scrolling-tabs-container {
- position: relative;
- }
-}
-
.deploy-key {
// Ensure that the fingerprint does not overflow on small screens
.fingerprint {
diff --git a/app/assets/stylesheets/pages/repo.scss b/app/assets/stylesheets/pages/repo.scss
index 3c7edb0d4bb..4b8a3db1d06 100644
--- a/app/assets/stylesheets/pages/repo.scss
+++ b/app/assets/stylesheets/pages/repo.scss
@@ -335,7 +335,6 @@
img {
max-width: 90%;
- max-height: 90%;
}
.isZoomable {
@@ -553,6 +552,10 @@
}
.multi-file-commit-list-item {
+ &.is-active {
+ background-color: $white-normal;
+ }
+
.multi-file-discard-btn {
display: none;
margin-top: -2px;
@@ -1129,7 +1132,7 @@
.ide-context-header {
.avatar {
- flex: 0 0 40px;
+ flex: 0 0 38px;
}
.ide-merge-requests-dropdown.dropdown-menu {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index a35c4ff7c80..765c926751a 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -18,7 +18,8 @@
.file-finder-input:hover,
.issuable-search-form:hover,
.search-text-input:hover,
-.form-control:hover {
+.form-control:hover,
+:not[readonly] {
border-color: lighten($dropdown-input-focus-border, 20%);
box-shadow: 0 0 4px lighten($search-input-focus-shadow-color, 20%);
}
@@ -28,7 +29,7 @@ input[type="checkbox"]:hover {
}
.search {
- margin: 4px 8px 0;
+ margin: 0 8px;
form {
@extend .form-control;
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 16e999341da..1f8e61257a9 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -127,12 +127,16 @@
color: $gl-danger;
}
-.service-settings .form-control-label {
- padding-top: 0;
+.service-settings {
+ input[type="radio"],
+ input[type="checkbox"] {
+ margin-top: 10px;
+ }
}
.integration-settings-form {
- .card.card-body {
+ .card.card-body,
+ .info-well {
padding: $gl-padding / 2;
box-shadow: none;
}
diff --git a/app/assets/stylesheets/pages/settings_ci_cd.scss b/app/assets/stylesheets/pages/settings_ci_cd.scss
index a355e2dee24..777fdb3581e 100644
--- a/app/assets/stylesheets/pages/settings_ci_cd.scss
+++ b/app/assets/stylesheets/pages/settings_ci_cd.scss
@@ -16,3 +16,12 @@
.registry-placeholder {
min-height: 60px;
}
+
+.auto-devops-card {
+ margin-bottom: $gl-vert-padding;
+
+ > .card-body {
+ border-radius: $card-border-radius;
+ padding: $gl-padding $gl-padding-24;
+ }
+}
diff --git a/app/assets/stylesheets/performance_bar.scss b/app/assets/stylesheets/performance_bar.scss
index 06ef58531d7..8cdf2275551 100644
--- a/app/assets/stylesheets/performance_bar.scss
+++ b/app/assets/stylesheets/performance_bar.scss
@@ -15,6 +15,7 @@
color: $perf-bar-text;
select {
+ color: $perf-bar-text;
width: 200px;
}
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index ea302f17d16..9aaec905734 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -41,6 +41,13 @@ class Admin::AppearancesController < Admin::ApplicationController
redirect_to admin_appearances_path, notice: 'Header logo was succesfully removed.'
end
+ def favicon
+ @appearance.remove_favicon!
+ @appearance.save
+
+ redirect_to admin_appearances_path, notice: 'Favicon was succesfully removed.'
+ end
+
private
# Use callbacks to share common setup or constraints between actions.
@@ -61,6 +68,8 @@ class Admin::AppearancesController < Admin::ApplicationController
logo_cache
header_logo
header_logo_cache
+ favicon
+ favicon_cache
new_project_guidelines
updated_by
]
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index bc60a0a02e8..56312f801fb 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -91,6 +91,10 @@ class ApplicationController < ActionController::Base
payload[:user_id] = logged_user.try(:id)
payload[:username] = logged_user.try(:username)
end
+
+ if response.status == 422 && response.body.present? && response.content_type == 'application/json'.freeze
+ payload[:response] = response.body
+ end
end
# Controllers such as GitHttpController may use alternative methods
@@ -280,8 +284,10 @@ class ApplicationController < ActionController::Base
return unless current_user
return if current_user.terms_accepted?
+ message = _("Please accept the Terms of Service before continuing.")
+
if sessionless_user?
- render_403
+ access_denied!(message)
else
# Redirect to the destination if the request is a get.
# Redirect to the source if it was a post, so the user can re-submit after
@@ -292,7 +298,7 @@ class ApplicationController < ActionController::Base
URI(request.referer).path if request.referer
end
- flash[:notice] = _("Please accept the Terms of Service before continuing.")
+ flash[:notice] = message
redirect_to terms_path(redirect: redirect_path), status: :found
end
end
diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb
index 381fd4d7508..e8b5934f2a9 100644
--- a/app/controllers/boards/lists_controller.rb
+++ b/app/controllers/boards/lists_controller.rb
@@ -56,8 +56,12 @@ module Boards
private
+ def list_creation_attrs
+ %i[label_id]
+ end
+
def list_params
- params.require(:list).permit(:label_id)
+ params.require(:list).permit(list_creation_attrs)
end
def move_params
@@ -65,11 +69,15 @@ module Boards
end
def serialize_as_json(resource)
- resource.as_json(
+ resource.as_json(serialization_attrs)
+ end
+
+ def serialization_attrs
+ {
only: [:id, :list_type, :position],
methods: [:title],
label: true
- )
+ }
end
end
end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index c925b4aada5..d04eb192129 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -7,6 +7,19 @@ module IssuableActions
before_action :authorize_admin_issuable!, only: :bulk_update
end
+ def permitted_keys
+ [
+ :issuable_ids,
+ :assignee_id,
+ :milestone_id,
+ :state_event,
+ :subscription_event,
+ label_ids: [],
+ add_label_ids: [],
+ remove_label_ids: []
+ ]
+ end
+
def show
respond_to do |format|
format.html
@@ -140,24 +153,15 @@ module IssuableActions
end
def bulk_update_params
- permitted_keys = [
- :issuable_ids,
- :assignee_id,
- :milestone_id,
- :state_event,
- :subscription_event,
- label_ids: [],
- add_label_ids: [],
- remove_label_ids: []
- ]
+ permitted_keys_array = permitted_keys.dup
if resource_name == 'issue'
- permitted_keys << { assignee_ids: [] }
+ permitted_keys_array << { assignee_ids: [] }
else
- permitted_keys.unshift(:assignee_id)
+ permitted_keys_array.unshift(:assignee_id)
end
- params.require(:update).permit(permitted_keys)
+ params.require(:update).permit(permitted_keys_array)
end
def resource_name
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index ca1b80a36a0..2ef2ee76855 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -95,12 +95,7 @@ module IssuableCollections
elsif @group
@filter_params[:group_id] = @group.id
@filter_params[:include_subgroups] = true
- else
- # TODO: this filter ignore issues/mr created in public or
- # internal repos where you are not a member. Enable this filter
- # or improve current implementation to filter only issues you
- # created or assigned or mentioned
- # @filter_params[:authorized_only] = true
+ @filter_params[:use_cte_for_search] = true
end
@filter_params.permit(finder_type.valid_params)
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index b9b9b6e4e88..170bca8b56f 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -2,7 +2,7 @@ module UploadsActions
include Gitlab::Utils::StrongMemoize
include SendFileUpload
- UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo).freeze
+ UPLOAD_MOUNTS = %w(avatar attachment file logo header_logo favicon).freeze
def create
link_to_file = UploadService.new(model, params[:file], uploader_class).execute
@@ -31,6 +31,11 @@ module UploadsActions
disposition = uploader.image_or_video? ? 'inline' : 'attachment'
+ uploaders = [uploader, *uploader.versions.values]
+ uploader = uploaders.find { |version| version.filename == params[:filename] }
+
+ return render_404 unless uploader
+
send_upload(uploader, attachment: uploader.filename, disposition: disposition)
end
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index 58be330f466..863f50e8e66 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -2,6 +2,7 @@ class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction
before_action :label, only: [:edit, :update, :destroy]
+ before_action :available_labels, only: [:index]
before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy]
before_action :save_previous_label_path, only: [:edit]
@@ -12,17 +13,8 @@ class Groups::LabelsController < Groups::ApplicationController
format.html do
@labels = @group.labels.page(params[:page])
end
-
format.json do
- available_labels = LabelsFinder.new(
- current_user,
- group_id: @group.id,
- only_group_labels: params[:only_group_labels],
- include_ancestor_groups: params[:include_ancestor_groups],
- include_descendant_groups: params[:include_descendant_groups]
- ).execute
-
- render json: LabelSerializer.new.represent_appearance(available_labels)
+ render json: LabelSerializer.new.represent_appearance(@available_labels)
end
end
end
@@ -113,4 +105,15 @@ class Groups::LabelsController < Groups::ApplicationController
def save_previous_label_path
session[:previous_labels_path] = URI(request.referer || '').path
end
+
+ def available_labels
+ @available_labels ||=
+ LabelsFinder.new(
+ current_user,
+ group_id: @group.id,
+ only_group_labels: params[:only_group_labels],
+ include_ancestor_groups: params[:include_ancestor_groups],
+ include_descendant_groups: params[:include_descendant_groups]
+ ).execute
+ end
end
diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb
index 4d758402850..a5c82caa897 100644
--- a/app/controllers/projects/clusters/applications_controller.rb
+++ b/app/controllers/projects/clusters/applications_controller.rb
@@ -42,6 +42,6 @@ class Projects::Clusters::ApplicationsController < Projects::ApplicationControll
owner: current_user
}
- Applications::CreateService.new(current_user, oauth_application_params).execute
+ Applications::CreateService.new(current_user, oauth_application_params).execute(request)
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index 1d850baf012..fb3f6eec2bd 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -41,7 +41,7 @@ module Projects
:runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
:auto_cancel_pending_pipelines, :ci_config_path,
- auto_devops_attributes: [:id, :domain, :enabled]
+ auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy]
)
end
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index f5a222b3a48..e6d6965036e 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -3,6 +3,9 @@ class RegistrationsController < Devise::RegistrationsController
include AcceptsPendingInvitations
before_action :whitelist_query_limiting, only: [:destroy]
+ before_action :ensure_terms_accepted,
+ if: -> { Gitlab::CurrentSettings.current_application_settings.enforce_terms? },
+ only: [:create]
def new
redirect_to(new_user_session_path)
@@ -18,7 +21,9 @@ class RegistrationsController < Devise::RegistrationsController
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
accept_pending_invitations
- super
+ super do |new_user|
+ persist_accepted_terms_if_required(new_user)
+ end
else
flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
flash.delete :recaptcha_error
@@ -40,6 +45,16 @@ class RegistrationsController < Devise::RegistrationsController
protected
+ def persist_accepted_terms_if_required(new_user)
+ return unless new_user.persisted?
+ return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms?
+
+ if terms_accepted?
+ terms = ApplicationSetting::Term.latest
+ Users::RespondToTermsService.new(new_user, terms).execute(accepted: true)
+ end
+ end
+
def destroy_confirmation_valid?
if current_user.confirm_deletion_with_password?
current_user.valid_password?(params[:password])
@@ -91,4 +106,14 @@ class RegistrationsController < Devise::RegistrationsController
def whitelist_query_limiting
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42380')
end
+
+ def ensure_terms_accepted
+ return if terms_accepted?
+
+ redirect_to new_user_session_path, alert: _('You must accept our Terms of Service and privacy policy in order to register an account')
+ end
+
+ def terms_accepted?
+ Gitlab::Utils.to_boolean(params[:terms_opt_in])
+ end
end
diff --git a/app/controllers/users/terms_controller.rb b/app/controllers/users/terms_controller.rb
index f7c6d1d59db..1b1560a2a00 100644
--- a/app/controllers/users/terms_controller.rb
+++ b/app/controllers/users/terms_controller.rb
@@ -2,6 +2,7 @@ module Users
class TermsController < ApplicationController
include InternalRedirect
+ skip_before_action :authenticate_user!
skip_before_action :enforce_terms!
skip_before_action :check_password_expiration
skip_before_action :check_two_factor_requirement
@@ -14,7 +15,7 @@ module Users
def index
@redirect = redirect_path
- if @term.accepted_by_user?(current_user)
+ if current_user && @term.accepted_by_user?(current_user)
flash.now[:notice] = "You have already accepted the Terms of Service as #{current_user.to_reference}"
end
end
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index 067aff408df..2a656c0d31c 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -3,17 +3,29 @@ class GroupMembersFinder
@group = group
end
- def execute
+ def execute(include_descendants: false)
group_members = @group.members
+ wheres = []
- return group_members unless @group.parent
+ return group_members unless @group.parent || include_descendants
- parents_members = GroupMember.non_request
- .where(source_id: @group.ancestors.select(:id))
- .where.not(user_id: @group.users.select(:id))
+ wheres << "members.id IN (#{group_members.select(:id).to_sql})"
- wheres = ["members.id IN (#{group_members.select(:id).to_sql})"]
- wheres << "members.id IN (#{parents_members.select(:id).to_sql})"
+ if @group.parent
+ parents_members = GroupMember.non_request
+ .where(source_id: @group.ancestors.select(:id))
+ .where.not(user_id: @group.users.select(:id))
+
+ wheres << "members.id IN (#{parents_members.select(:id).to_sql})"
+ end
+
+ if include_descendants
+ descendant_members = GroupMember.non_request
+ .where(source_id: @group.descendants.select(:id))
+ .where.not(user_id: @group.users.select(:id))
+
+ wheres << "members.id IN (#{descendant_members.select(:id).to_sql})"
+ end
GroupMember.where(wheres.join(' OR '))
end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index c6ef79ce15e..5d5f72c4d86 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -23,6 +23,7 @@
# created_before: datetime
# updated_after: datetime
# updated_before: datetime
+# use_cte_for_search: boolean
#
class IssuableFinder
prepend FinderWithCrossProjectAccess
@@ -54,6 +55,7 @@ class IssuableFinder
sort
state
include_subgroups
+ use_cte_for_search
]
end
@@ -74,19 +76,21 @@ class IssuableFinder
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)
+ # This has to be last as we may use a CTE as an optimization fence by
+ # passing the use_cte_for_search param
+ # https://www.postgresql.org/docs/current/static/queries-with.html
+ items = by_search(items)
sort(items)
end
def filter_items(items)
+ items = by_project(items)
items = by_scope(items)
items = by_created_at(items)
items = by_updated_at(items)
items = by_state(items)
items = by_group(items)
- items = by_search(items)
items = by_assignee(items)
items = by_author(items)
items = by_non_archived(items)
@@ -107,7 +111,6 @@ class IssuableFinder
#
def count_by_state
count_params = params.merge(state: nil, sort: nil)
- labels_count = label_names.any? ? label_names.count : 1
finder = self.class.new(current_user, count_params)
counts = Hash.new(0)
@@ -116,6 +119,11 @@ class IssuableFinder
# per issuable, so we have to count those in Ruby - which is bad, but still
# better than performing multiple queries.
#
+ # This does not apply when we are using a CTE for the search, as the labels
+ # GROUP BY is inside the subquery in that case, so we set labels_count to 1.
+ labels_count = label_names.any? ? label_names.count : 1
+ labels_count = 1 if use_cte_for_search?
+
finder.execute.reorder(nil).group(:state).count.each do |key, value|
counts[Array(key).last.to_sym] += value / labels_count
end
@@ -159,10 +167,7 @@ class IssuableFinder
finder_options = { include_subgroups: params[:include_subgroups], only_owned: true }
GroupProjectsFinder.new(group: group, current_user: current_user, options: finder_options).execute
else
- opts = { current_user: current_user }
- opts[:project_ids_relation] = item_project_ids(items) if items
-
- ProjectsFinder.new(opts).execute
+ ProjectsFinder.new(current_user: current_user).execute
end
@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil)
@@ -329,8 +334,24 @@ class IssuableFinder
items
end
+ def use_cte_for_search?
+ return false unless search
+ return false unless Gitlab::Database.postgresql?
+
+ params[:use_cte_for_search]
+ end
+
def by_search(items)
- search ? items.full_search(search) : items
+ return items unless search
+
+ if use_cte_for_search?
+ cte = Gitlab::SQL::RecursiveCTE.new(klass.table_name)
+ cte << items
+
+ items = klass.with(cte.to_arel).from(klass.table_name)
+ end
+
+ items.full_search(search)
end
def by_iids(items)
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index 3626670d141..24a6b9349a0 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -136,8 +136,4 @@ class IssuesFinder < IssuableFinder
items
end
end
-
- def item_project_ids(items)
- items&.reorder(nil)&.select(:project_id)
- end
end
diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb
index 4734d97b8c7..4c893ae2de6 100644
--- a/app/finders/members_finder.rb
+++ b/app/finders/members_finder.rb
@@ -7,12 +7,12 @@ class MembersFinder
@group = project.group
end
- def execute
+ def execute(include_descendants: false)
project_members = project.project_members
project_members = project_members.non_invite unless can?(current_user, :admin_project, project)
if group
- group_members = GroupMembersFinder.new(group).execute
+ group_members = GroupMembersFinder.new(group).execute(include_descendants: include_descendants)
group_members = group_members.non_invite
union = Gitlab::SQL::Union.new([project_members, group_members], remove_duplicates: false)
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index e2240e5e0d8..8d84ed4bdfb 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -56,8 +56,4 @@ class MergeRequestsFinder < IssuableFinder
items.where(target_branch: target_branch)
end
-
- def item_project_ids(items)
- items&.reorder(nil)&.select(:target_project_id)
- end
end
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index d498a2d6d11..9d3772d7541 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -54,7 +54,10 @@ class SnippetsFinder < UnionFinder
end
def authorized_snippets
- Snippet.where(feature_available_projects.or(not_project_related))
+ # This query was intentionally converted to a raw one to get it work in Rails 5.0.
+ # In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
+ # Please convert it back when on rails 5.2 as it works again as expected since 5.2.
+ Snippet.where("#{feature_available_projects} OR #{not_project_related}")
.public_or_visible_to_user(current_user)
end
@@ -86,18 +89,20 @@ class SnippetsFinder < UnionFinder
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)
+ return table[:id].eq(nil).to_sql unless Ability.allowed?(current_user, :read_cross_project)
projects = projects_for_user 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)
+ # This query was intentionally converted to a raw one to get it work in Rails 5.0.
+ # In Rails 5.0 and 5.1 there's a bug: https://github.com/rails/arel/issues/531
+ # Please convert it back when on rails 5.2 as it works again as expected since 5.2.
+ "snippets.project_id IN (#{projects.to_sql})"
end
def not_project_related
- table[:project_id].eq(nil)
+ table[:project_id].eq(nil).to_sql
end
def table
diff --git a/app/helpers/favicon_helper.rb b/app/helpers/favicon_helper.rb
new file mode 100644
index 00000000000..3a5342a8d9d
--- /dev/null
+++ b/app/helpers/favicon_helper.rb
@@ -0,0 +1,7 @@
+module FaviconHelper
+ def favicon_extension_whitelist
+ FaviconUploader::EXTENSION_WHITELIST
+ .map { |extension| "'.#{extension}'"}
+ .to_sentence
+ end
+end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 58372edff3c..2f304b040c7 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -60,7 +60,7 @@ module IconsHelper
def spinner(text = nil, visible = false)
css_class = 'loading'
- css_class << ' hidden' unless visible
+ css_class << ' hide' unless visible
content_tag :div, class: css_class do
icon('spinner spin') + text
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 8572c2b7276..d42284868c7 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -165,7 +165,7 @@ module IssuablesHelper
output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip', title: _('1st contribution!'))
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block")
- output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none d-lg-none d-xl-inline-block")
+ output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
output.html_safe
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index e1b0e7a4a3e..c7df25cecef 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -211,6 +211,14 @@ module LabelsHelper
end
end
+ def label_status_tooltip(label, status)
+ type = label.is_a?(ProjectLabel) ? 'project' : 'group'
+ level = status.unsubscribed? ? type : status.sub('-level', '')
+ action = status.unsubscribed? ? 'Subscribe' : 'Unsubscribe'
+
+ "#{action} at #{level} level"
+ end
+
# Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once
end
diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb
index a8397b03d63..68d892393ef 100644
--- a/app/helpers/page_layout_helper.rb
+++ b/app/helpers/page_layout_helper.rb
@@ -39,10 +39,7 @@ module PageLayoutHelper
end
def favicon
- return 'favicon-yellow.ico' if Gitlab::Utils.to_boolean(ENV['CANARY'])
- return 'favicon-blue.ico' if Rails.env.development?
-
- 'favicon.ico'
+ Gitlab::Favicon.main
end
def page_image
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index cdbb572f80a..daad829faa2 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -171,11 +171,12 @@ module ProjectsHelper
key = [
project.route.cache_key,
project.cache_key,
+ project.last_activity_date,
controller.controller_name,
controller.action_name,
Gitlab::CurrentSettings.cache_key,
"cross-project:#{can?(current_user, :read_cross_project)}",
- 'v2.5'
+ 'v2.6'
]
key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index 67cc84a9140..b770aadef0e 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -14,6 +14,7 @@ class Appearance < ActiveRecord::Base
mount_uploader :logo, AttachmentUploader
mount_uploader :header_logo, AttachmentUploader
+ mount_uploader :favicon, FaviconUploader
# Overrides CacheableAttributes.current_without_cache
def self.current_without_cache
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 2d675726939..41446946a5e 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -611,6 +611,7 @@ module Ci
variables
.concat(pipeline.persisted_variables)
.append(key: 'CI_JOB_ID', value: id.to_s)
+ .append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
.append(key: 'CI_JOB_TOKEN', value: token, public: false)
.append(key: 'CI_BUILD_ID', value: id.to_s)
.append(key: 'CI_BUILD_TOKEN', value: token, public: false)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index eecd86349e4..f430f18ca9a 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -8,6 +8,7 @@ module Ci
include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize
include AtomicInternalId
+ include EnumWithNil
belongs_to :project, inverse_of: :pipelines
belongs_to :user
@@ -54,7 +55,7 @@ module Ci
after_create :keep_around_commits, unless: :importing?
- enum source: {
+ enum_with_nil source: {
unknown: nil,
push: 1,
web: 2,
@@ -64,7 +65,7 @@ module Ci
external: 6
}
- enum config_source: {
+ enum_with_nil config_source: {
unknown_source: nil,
repository_source: 1,
auto_devops_source: 2
@@ -548,7 +549,10 @@ module Ci
def persisted_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables.append(key: 'CI_PIPELINE_ID', value: id.to_s) if persisted?
+ break variables unless persisted?
+
+ variables.append(key: 'CI_PIPELINE_ID', value: id.to_s)
+ variables.append(key: 'CI_PIPELINE_URL', value: Gitlab::Routing.url_helpers.project_pipeline_url(project, self))
end
end
@@ -599,17 +603,6 @@ module Ci
@latest_builds_with_artifacts ||= builds.latest.with_artifacts_archive.to_a
end
- # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil.
- # They always return `false`.
- # These methods overwrite autogenerated ones to return correct results.
- def unknown?
- Gitlab.rails5? ? source.nil? : super
- end
-
- def unknown_source?
- Gitlab.rails5? ? config_source.nil? : super
- end
-
private
def ci_yaml_from_repo
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index a7d05722287..97516079b66 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -3,6 +3,7 @@ class CommitStatus < ActiveRecord::Base
include Importable
include AfterCommitQueue
include Presentable
+ include EnumWithNil
self.table_name = 'ci_builds'
@@ -39,7 +40,7 @@ class CommitStatus < ActiveRecord::Base
scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
scope :after_stage, -> (index) { where('stage_idx > ?', index) }
- enum failure_reason: {
+ enum_with_nil failure_reason: {
unknown_failure: nil,
script_failure: 1,
api_failure: 2,
@@ -190,11 +191,4 @@ class CommitStatus < ActiveRecord::Base
v =~ /\d+/ ? v.to_i : v
end
end
-
- # Rails 5.0 autogenerated question mark enum methods return wrong result if enum value is nil.
- # They always return `false`.
- # This method overwrites the autogenerated one to return correct result.
- def unknown_failure?
- Gitlab.rails5? ? failure_reason.nil? : super
- end
end
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index db8cf322ef7..9f6358cecbe 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -114,7 +114,7 @@ module CacheMarkdownField
end
def latest_cached_markdown_version
- return CacheMarkdownField::CACHE_REDCARPET_VERSION unless cached_markdown_version
+ return CacheMarkdownField::CACHE_COMMONMARK_VERSION unless cached_markdown_version
if cached_markdown_version < CacheMarkdownField::CACHE_COMMONMARK_VERSION_START
CacheMarkdownField::CACHE_REDCARPET_VERSION
diff --git a/app/models/concerns/enum_with_nil.rb b/app/models/concerns/enum_with_nil.rb
new file mode 100644
index 00000000000..6b37903da20
--- /dev/null
+++ b/app/models/concerns/enum_with_nil.rb
@@ -0,0 +1,33 @@
+module EnumWithNil
+ extend ActiveSupport::Concern
+
+ included do
+ def self.enum_with_nil(definitions)
+ # use original `enum` to auto-define all methods
+ enum(definitions)
+
+ # override auto-defined methods only for the
+ # key which uses nil value
+ definitions.each do |name, values|
+ next unless key_with_nil = values.key(nil)
+
+ # E.g. for enum_with_nil failure_reason: { unknown_failure: nil }
+ # this overrides auto-generated method `unknown_failure?`
+ define_method("#{key_with_nil}?") do
+ Gitlab.rails5? ? self[name].nil? : super()
+ end
+
+ # E.g. for enum_with_nil failure_reason: { unknown_failure: nil }
+ # this overrides auto-generated method `failure_reason`
+ define_method(name) do
+ orig = super()
+
+ return orig unless Gitlab.rails5?
+ return orig unless orig.nil?
+
+ self.class.public_send(name.to_s.pluralize).key(nil) # rubocop:disable GitlabSecurity/PublicSend
+ end
+ end
+ end
+ end
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index 8fb77a7869d..9c171de7fc3 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -11,6 +11,7 @@ class Group < Namespace
include GroupDescendant
include TokenAuthenticatable
include WithUploads
+ include Gitlab::Utils::StrongMemoize
has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
alias_method :members, :group_members
@@ -26,7 +27,11 @@ class Group < Namespace
has_many :milestones
has_many :project_group_links, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :shared_projects, through: :project_group_links, source: :project
+
+ # Overridden on another method
+ # Left here just to be dependent: :destroy
has_many :notification_settings, dependent: :destroy, as: :source # rubocop:disable Cop/ActiveRecordDependent
+
has_many :labels, class_name: 'GroupLabel'
has_many :variables, class_name: 'Ci::GroupVariable'
has_many :custom_attributes, class_name: 'GroupCustomAttribute'
@@ -88,6 +93,15 @@ class Group < Namespace
end
end
+ # Overrides notification_settings has_many association
+ # This allows to apply notification settings from parent groups
+ # to child groups and projects.
+ def notification_settings
+ source_type = self.class.base_class.name
+
+ NotificationSetting.where(source_type: source_type, source_id: self_and_ancestors_ids)
+ end
+
def to_reference(_from = nil, full: nil)
"#{self.class.reference_prefix}#{full_path}"
end
@@ -141,13 +155,14 @@ class Group < Namespace
)
end
- def add_user(user, access_level, current_user: nil, expires_at: nil)
+ def add_user(user, access_level, current_user: nil, expires_at: nil, ldap: false)
GroupMember.add_user(
self,
user,
access_level,
current_user: current_user,
- expires_at: expires_at
+ expires_at: expires_at,
+ ldap: ldap
)
end
@@ -195,6 +210,10 @@ class Group < Namespace
owners.include?(user) && owners.size == 1
end
+ def ldap_synced?
+ false
+ end
+
def post_create_hook
Gitlab::AppLogger.info("Group \"#{name}\" was created")
@@ -220,6 +239,12 @@ class Group < Namespace
members_with_parents.pluck(:user_id)
end
+ def self_and_ancestors_ids
+ strong_memoize(:self_and_ancestors_ids) do
+ self_and_ancestors.pluck(:id)
+ end
+ end
+
def members_with_parents
# Avoids an unnecessary SELECT when the group has no parents
source_ids =
diff --git a/app/models/label.rb b/app/models/label.rb
index de7f1d56c64..1cf04976602 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -137,6 +137,10 @@ class Label < ActiveRecord::Base
priority.try(:priority)
end
+ def priority?
+ priorities.present?
+ end
+
def template?
template
end
diff --git a/app/models/list.rb b/app/models/list.rb
index 5daf35ef845..4edcfa78835 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -2,17 +2,27 @@ class List < ActiveRecord::Base
belongs_to :board
belongs_to :label
- enum list_type: { backlog: 0, label: 1, closed: 2 }
+ enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3 }
validates :board, :list_type, presence: true
validates :label, :position, presence: true, if: :label?
validates :label_id, uniqueness: { scope: :board_id }, if: :label?
- validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :label?
+ validates :position, numericality: { only_integer: true, greater_than_or_equal_to: 0 }, if: :movable?
before_destroy :can_be_destroyed
- scope :destroyable, -> { where(list_type: list_types[:label]) }
- scope :movable, -> { where(list_type: list_types[:label]) }
+ scope :destroyable, -> { where(list_type: list_types.slice(*destroyable_types).values) }
+ scope :movable, -> { where(list_type: list_types.slice(*movable_types).values) }
+
+ class << self
+ def destroyable_types
+ [:label]
+ end
+
+ def movable_types
+ [:label]
+ end
+ end
def destroyable?
label?
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 535a2c362f2..324065c1162 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1125,8 +1125,11 @@ class MergeRequest < ActiveRecord::Base
project.merge_requests.merged.where(author_id: author_id).empty?
end
+ # TODO: remove once production database rename completes
+ alias_attribute :allow_collaboration, :allow_maintainer_to_push
+
def allow_collaboration
- collaborative_push_possible? && super
+ collaborative_push_possible? && allow_maintainer_to_push
end
alias_method :allow_collaboration?, :allow_collaboration
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 2c3580bbdc6..1a03dd9df56 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -1,4 +1,6 @@
class NotificationRecipient
+ include Gitlab::Utils::StrongMemoize
+
attr_reader :user, :type, :reason
def initialize(user, type, **opts)
unless NotificationSetting.levels.key?(type) || type == :subscription
@@ -64,7 +66,7 @@ class NotificationRecipient
return false unless @target
return false unless @target.respond_to?(:subscriptions)
- subscription = @target.subscriptions.find_by_user_id(@user.id)
+ subscription = @target.subscriptions.find { |subscription| subscription.user_id == @user.id }
subscription && !subscription.subscribed
end
@@ -142,10 +144,33 @@ class NotificationRecipient
return project_setting unless project_setting.nil? || project_setting.global?
- group_setting = @group && user.notification_settings_for(@group)
+ group_setting = closest_non_global_group_notification_settting
- return group_setting unless group_setting.nil? || group_setting.global?
+ return group_setting unless group_setting.nil?
user.global_notification_setting
end
+
+ # Returns the notificaton_setting of the lowest group in hierarchy with non global level
+ def closest_non_global_group_notification_settting
+ return unless @group
+ return if indexed_group_notification_settings.empty?
+
+ notification_setting = nil
+
+ @group.self_and_ancestors_ids.each do |id|
+ notification_setting = indexed_group_notification_settings[id]
+ break if notification_setting
+ end
+
+ notification_setting
+ end
+
+ def indexed_group_notification_settings
+ strong_memoize(:indexed_group_notification_settings) do
+ @group.notification_settings.where(user_id: user.id)
+ .where.not(level: NotificationSetting.levels[:global])
+ .index_by(&:source_id)
+ end
+ end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index fb6e9fb2676..e5fa1c4db7b 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -292,6 +292,7 @@ class Project < ActiveRecord::Base
validates :name, uniqueness: { scope: :namespace_id }
validates :import_url, url: { protocols: %w(http https ssh git),
allow_localhost: false,
+ enforce_user: true,
ports: VALID_IMPORT_PORTS }, if: [:external_import?, :import_url_changed?]
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
validate :check_limit, on: :create
@@ -675,6 +676,12 @@ class Project < ActiveRecord::Base
end
end
+ def human_import_status_name
+ ensure_import_state
+
+ import_state.human_status_name
+ end
+
def import_schedule
ensure_import_state(force: true)
@@ -1609,6 +1616,7 @@ class Project < ActiveRecord::Base
def after_import
repository.after_import
+ wiki.repository.after_import
import_finish
remove_import_jid
update_project_counter_caches
@@ -2132,10 +2140,14 @@ class Project < ActiveRecord::Base
check_access = -> do
next false if empty_repo?
- merge_request = source_of_merge_requests.opened
- .where(allow_collaboration: true)
- .find_by(source_branch: branch_name)
- merge_request&.can_be_merged_by?(user)
+ merge_requests = source_of_merge_requests.opened
+ .where(allow_collaboration: true)
+
+ if branch_name
+ merge_requests.find_by(source_branch: branch_name)&.can_be_merged_by?(user)
+ else
+ merge_requests.any? { |merge_request| merge_request.can_be_merged_by?(user) }
+ end
end
if RequestStore.active?
diff --git a/app/models/project_auto_devops.rb b/app/models/project_auto_devops.rb
index ed6c1eddbc1..d7d6aaceb27 100644
--- a/app/models/project_auto_devops.rb
+++ b/app/models/project_auto_devops.rb
@@ -1,11 +1,18 @@
class ProjectAutoDevops < ActiveRecord::Base
belongs_to :project
+ enum deploy_strategy: {
+ continuous: 0,
+ manual: 1
+ }
+
scope :enabled, -> { where(enabled: true) }
scope :disabled, -> { where(enabled: false) }
validates :domain, allow_blank: true, hostname: { allow_numeric_hostname: true }
+ after_save :create_gitlab_deploy_token, if: :needs_to_create_deploy_token?
+
def instance_domain
Gitlab::CurrentSettings.auto_devops_domain
end
@@ -20,6 +27,30 @@ class ProjectAutoDevops < ActiveRecord::Base
variables.append(key: 'AUTO_DEVOPS_DOMAIN',
value: domain.presence || instance_domain)
end
+
+ if manual?
+ variables.append(key: 'STAGING_ENABLED', value: 1)
+ variables.append(key: 'INCREMENTAL_ROLLOUT_ENABLED', value: 1)
+ end
end
end
+
+ private
+
+ def create_gitlab_deploy_token
+ project.deploy_tokens.create!(
+ name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME,
+ read_registry: true
+ )
+ end
+
+ def needs_to_create_deploy_token?
+ auto_devops_enabled? &&
+ !project.public? &&
+ !project.deploy_tokens.find_by(name: DeployToken::GITLAB_DEPLOY_TOKEN_NAME).present?
+ end
+
+ def auto_devops_enabled?
+ Gitlab::CurrentSettings.auto_devops_enabled? || enabled?
+ end
end
diff --git a/app/models/project_services/chat_message/base_message.rb b/app/models/project_services/chat_message/base_message.rb
index 22a65b5145e..f710fa85b5d 100644
--- a/app/models/project_services/chat_message/base_message.rb
+++ b/app/models/project_services/chat_message/base_message.rb
@@ -26,13 +26,18 @@ module ChatMessage
end
end
- def pretext
+ def summary
return message if markdown
format(message)
end
+ def pretext
+ summary
+ end
+
def fallback
+ format(message)
end
def attachments
diff --git a/app/models/project_services/chat_message/pipeline_message.rb b/app/models/project_services/chat_message/pipeline_message.rb
index 2135122278a..96fd23aede3 100644
--- a/app/models/project_services/chat_message/pipeline_message.rb
+++ b/app/models/project_services/chat_message/pipeline_message.rb
@@ -23,10 +23,6 @@ module ChatMessage
''
end
- def fallback
- format(message)
- end
-
def attachments
return message if markdown
diff --git a/app/models/project_services/gemnasium_service.rb b/app/models/project_services/gemnasium_service.rb
index 84248f9590b..8a6b0ed1a5f 100644
--- a/app/models/project_services/gemnasium_service.rb
+++ b/app/models/project_services/gemnasium_service.rb
@@ -43,13 +43,18 @@ class GemnasiumService < Service
def execute(data)
return unless supported_events.include?(data[:object_kind])
+ # Gitaly: this class will be removed https://gitlab.com/gitlab-org/gitlab-ee/issues/6010
+ repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path_to_repo
+ end
+
Gemnasium::GitlabService.execute(
ref: data[:ref],
before: data[:before],
after: data[:after],
token: token,
api_key: api_key,
- repo: project.repository.path_to_repo # Gitaly: fixed by https://gitlab.com/gitlab-org/security-products/gemnasium-migration/issues/9
+ repo: repo_path
)
end
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index eb3261c902f..412d62388f0 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -265,7 +265,7 @@ class JiraService < IssueTrackerService
title: title,
status: status,
icon: {
- title: 'GitLab', url16x16: asset_url('favicon.ico', host: gitlab_config.url)
+ title: 'GitLab', url16x16: asset_url(Gitlab::Favicon.main, host: gitlab_config.url)
}
}
}
diff --git a/app/models/project_services/microsoft_teams_service.rb b/app/models/project_services/microsoft_teams_service.rb
index 2facff53e26..99500caec0e 100644
--- a/app/models/project_services/microsoft_teams_service.rb
+++ b/app/models/project_services/microsoft_teams_service.rb
@@ -44,7 +44,7 @@ class MicrosoftTeamsService < ChatNotificationService
def notify(message, opts)
MicrosoftTeams::Notifier.new(webhook).ping(
title: message.project_name,
- pretext: message.pretext,
+ summary: message.summary,
activity: message.activity,
attachments: message.attachments
)
diff --git a/app/models/remote_mirror.rb b/app/models/remote_mirror.rb
index 5cd222e18a4..c4b5dd2dc96 100644
--- a/app/models/remote_mirror.rb
+++ b/app/models/remote_mirror.rb
@@ -16,7 +16,7 @@ class RemoteMirror < ActiveRecord::Base
belongs_to :project, inverse_of: :remote_mirrors
- validates :url, presence: true, url: { protocols: %w(ssh git http https), allow_blank: true }
+ validates :url, presence: true, url: { protocols: %w(ssh git http https), allow_blank: true, enforce_user: true }
before_save :set_new_remote_name, if: :mirror_url_changed?
diff --git a/app/models/timelog.rb b/app/models/timelog.rb
index f4c5c581a11..659146f43e4 100644
--- a/app/models/timelog.rb
+++ b/app/models/timelog.rb
@@ -19,4 +19,9 @@ class Timelog < ActiveRecord::Base
errors.add(:base, 'Issue or Merge Request ID is required')
end
end
+
+ # Rails5 defaults to :touch_later, overwrite for normal touch
+ def belongs_to_touch_method
+ :touch
+ end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index e219ab800ad..8e0dc91b2a7 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1038,7 +1038,10 @@ class User < ActiveRecord::Base
def notification_settings_for(source)
if notification_settings.loaded?
- notification_settings.find { |notification| notification.source == source }
+ notification_settings.find do |notification|
+ notification.source_type == source.class.base_class.name &&
+ notification.source_id == source.id
+ end
else
notification_settings.find_or_initialize_by(source: source)
end
diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb
index 8e8bda2f9df..47df7f9dcf9 100644
--- a/app/serializers/status_entity.rb
+++ b/app/serializers/status_entity.rb
@@ -7,16 +7,7 @@ class StatusEntity < Grape::Entity
expose :details_path
expose :favicon do |status|
- dir =
- if Gitlab::Utils.to_boolean(ENV['CANARY'])
- File.join('ci_favicons', 'canary')
- elsif Rails.env.development?
- File.join('ci_favicons', 'dev')
- else
- 'ci_favicons'
- end
-
- ActionController::Base.helpers.image_path(File.join(dir, "#{status.favicon}.ico"))
+ Gitlab::Favicon.status_overlay(status.favicon)
end
expose :action, if: -> (status, _) { status.has_action? } do
diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb
index e67af929954..94a434b95dd 100644
--- a/app/services/applications/create_service.rb
+++ b/app/services/applications/create_service.rb
@@ -5,7 +5,7 @@ module Applications
@params = params.except(:ip_address)
end
- def execute(request = nil)
+ def execute(request)
Doorkeeper::Application.create(@params)
end
end
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 5a961ac89e4..b1dbe73cdf7 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -3,13 +3,18 @@ module Boards
class ListService < Boards::BaseService
def execute
issues = IssuesFinder.new(current_user, filter_params).execute
- issues = without_board_labels(issues) unless movable_list? || closed_list?
- issues = with_list_label(issues) if movable_list?
+ issues = filter(issues)
issues.order_by_position_and_priority
end
private
+ def filter(issues)
+ issues = without_board_labels(issues) unless list&.movable? || list&.closed?
+ issues = with_list_label(issues) if list&.label?
+ issues
+ end
+
def board
@board ||= parent.boards.find(params[:board_id])
end
@@ -20,18 +25,6 @@ module Boards
@list = board.lists.find(params[:id]) if params.key?(:id)
end
- def movable_list?
- return @movable_list if defined?(@movable_list)
-
- @movable_list = list.present? && list.movable?
- end
-
- def closed_list?
- return @closed_list if defined?(@closed_list)
-
- @closed_list = list.present? && list.closed?
- end
-
def filter_params
set_parent
set_state
diff --git a/app/services/boards/issues/move_service.rb b/app/services/boards/issues/move_service.rb
index 3ceab209f3f..ee3112c7571 100644
--- a/app/services/boards/issues/move_service.rb
+++ b/app/services/boards/issues/move_service.rb
@@ -3,7 +3,7 @@ module Boards
class MoveService < Boards::BaseService
def execute(issue)
return false unless can?(current_user, :update_issue, issue)
- return false if issue_params.empty?
+ return false if issue_params(issue).empty?
update(issue)
end
@@ -28,10 +28,10 @@ module Boards
end
def update(issue)
- ::Issues::UpdateService.new(issue.project, current_user, issue_params).execute(issue)
+ ::Issues::UpdateService.new(issue.project, current_user, issue_params(issue)).execute(issue)
end
- def issue_params
+ def issue_params(issue)
attrs = {}
if move_between_lists?
diff --git a/app/services/boards/lists/create_service.rb b/app/services/boards/lists/create_service.rb
index 02f1c709374..6fd9885d4f3 100644
--- a/app/services/boards/lists/create_service.rb
+++ b/app/services/boards/lists/create_service.rb
@@ -1,16 +1,28 @@
module Boards
module Lists
class CreateService < Boards::BaseService
+ include Gitlab::Utils::StrongMemoize
+
def execute(board)
List.transaction do
- label = available_labels_for(board).find(params[:label_id])
+ target = target(board)
position = next_position(board)
- create_list(board, label, position)
+ create_list(board, type, target, position)
end
end
private
+ def type
+ :label
+ end
+
+ def target(board)
+ strong_memoize(:target) do
+ available_labels_for(board).find(params[:label_id])
+ end
+ end
+
def available_labels_for(board)
options = { include_ancestor_groups: true }
@@ -28,8 +40,8 @@ module Boards
max_position.nil? ? 0 : max_position.succ
end
- def create_list(board, label, position)
- board.lists.create(label: label, list_type: :label, position: position)
+ def create_list(board, type, target, position)
+ board.lists.create(type => target, list_type: type, position: position)
end
end
end
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index f96f2931508..4d0578becbe 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -17,7 +17,7 @@ module Commits
new_commit = create_commit!
success(result: new_commit)
- rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Gitlab::Git::CommitError, Gitlab::Git::HooksService::PreReceiveError => ex
+ rescue ValidationError, ChangeError, Gitlab::Git::Index::IndexError, Gitlab::Git::CommitError, Gitlab::Git::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index 0ba6a0ac6b5..9b1a4d960e2 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -14,7 +14,7 @@ class CreateBranchService < BaseService
else
error('Invalid reference name')
end
- rescue Gitlab::Git::HooksService::PreReceiveError => ex
+ rescue Gitlab::Git::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 1f059c31944..e1499dcee64 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -16,7 +16,7 @@ class DeleteBranchService < BaseService
else
error('Failed to remove branch')
end
- rescue Gitlab::Git::HooksService::PreReceiveError => ex
+ rescue Gitlab::Git::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/merge_requests/create_from_issue_service.rb b/app/services/merge_requests/create_from_issue_service.rb
index cf687b71d16..3407b312700 100644
--- a/app/services/merge_requests/create_from_issue_service.rb
+++ b/app/services/merge_requests/create_from_issue_service.rb
@@ -41,7 +41,9 @@ module MergeRequests
end
def ref
- @ref || project.default_branch || 'master'
+ return @ref if project.repository.branch_exists?(@ref)
+
+ project.default_branch || 'master'
end
def merge_request
diff --git a/app/services/merge_requests/ff_merge_service.rb b/app/services/merge_requests/ff_merge_service.rb
index ba6853b835a..bffc09c34f0 100644
--- a/app/services/merge_requests/ff_merge_service.rb
+++ b/app/services/merge_requests/ff_merge_service.rb
@@ -13,7 +13,7 @@ module MergeRequests
source,
merge_request.target_branch,
merge_request: merge_request)
- rescue Gitlab::Git::HooksService::PreReceiveError => e
+ rescue Gitlab::Git::PreReceiveError => e
raise MergeError, e.message
rescue StandardError => e
raise MergeError, "Something went wrong during merge: #{e.message}"
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 126da891c78..3d587f97906 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -79,7 +79,7 @@ module MergeRequests
message = params[:commit_message] || merge_request.merge_commit_message
repository.merge(current_user, source, merge_request, message)
- rescue Gitlab::Git::HooksService::PreReceiveError => e
+ rescue Gitlab::Git::PreReceiveError => e
handle_merge_error(log_message: e.message)
raise MergeError, 'Something went wrong during merge pre-receive hook'
rescue => e
diff --git a/app/services/projects/destroy_service.rb b/app/services/projects/destroy_service.rb
index de0125ed0dd..02769e72229 100644
--- a/app/services/projects/destroy_service.rb
+++ b/app/services/projects/destroy_service.rb
@@ -135,6 +135,7 @@ module Projects
raise_error('Failed to remove some tags in project container registry. Please try again or contact administrator.')
end
+ log_destroy_event
trash_repositories!
# Rails attempts to load all related records into memory before
@@ -148,6 +149,10 @@ module Projects
end
end
+ def log_destroy_event
+ log_info("Attempting to destroy #{project.full_path} (#{project.id})")
+ end
+
##
# This method makes sure that we correctly remove registry tags
# for legacy image repository (when repository path equals project path).
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 0215994b1a7..9ac8fdb4cff 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -561,6 +561,17 @@ module QuickActions
end
end
+ desc 'Make issue confidential.'
+ explanation do
+ 'Makes this issue confidential'
+ end
+ condition do
+ issuable.is_a?(Issue) && current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
+ end
+ command :confidential do
+ @updates[:confidential] = true
+ end
+
def extract_users(params)
return [] if params.nil?
diff --git a/app/services/tags/create_service.rb b/app/services/tags/create_service.rb
index cc76d0df3a1..3cc88d77ba1 100644
--- a/app/services/tags/create_service.rb
+++ b/app/services/tags/create_service.rb
@@ -13,7 +13,7 @@ module Tags
new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Gitlab::Git::Repository::TagExistsError
return error("Tag #{tag_name} already exists")
- rescue Gitlab::Git::HooksService::PreReceiveError => ex
+ rescue Gitlab::Git::PreReceiveError => ex
return error(ex.message)
end
diff --git a/app/services/tags/destroy_service.rb b/app/services/tags/destroy_service.rb
index d3d46064729..b84b061e460 100644
--- a/app/services/tags/destroy_service.rb
+++ b/app/services/tags/destroy_service.rb
@@ -21,7 +21,7 @@ module Tags
else
error('Failed to remove tag')
end
- rescue Gitlab::Git::HooksService::PreReceiveError => ex
+ rescue Gitlab::Git::PreReceiveError => ex
error(ex.message)
end
diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb
index 7d1ed768ee8..643f2ce1481 100644
--- a/app/services/validate_new_branch_service.rb
+++ b/app/services/validate_new_branch_service.rb
@@ -13,7 +13,7 @@ class ValidateNewBranchService < BaseService
end
success
- rescue Gitlab::Git::HooksService::PreReceiveError => ex
+ rescue Gitlab::Git::PreReceiveError => ex
error(ex.message)
end
end
diff --git a/app/uploaders/favicon_uploader.rb b/app/uploaders/favicon_uploader.rb
new file mode 100644
index 00000000000..09afc63a5aa
--- /dev/null
+++ b/app/uploaders/favicon_uploader.rb
@@ -0,0 +1,24 @@
+class FaviconUploader < AttachmentUploader
+ EXTENSION_WHITELIST = %w[png ico].freeze
+
+ include CarrierWave::MiniMagick
+
+ version :favicon_main do
+ process resize_to_fill: [32, 32]
+ process convert: 'png'
+
+ def full_filename(filename)
+ filename_for_different_format(super(filename), 'png')
+ end
+ end
+
+ def extension_whitelist
+ EXTENSION_WHITELIST
+ end
+
+ private
+
+ def filename_for_different_format(filename, format)
+ filename.chomp(File.extname(filename)) + ".#{format}"
+ end
+end
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index 133fdf6684d..36bc0a4575a 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -65,10 +65,10 @@ class FileUploader < GitlabUploader
SecureRandom.hex
end
- def upload_paths(filename)
+ def upload_paths(identifier)
[
- File.join(secret, filename),
- File.join(base_dir(Store::REMOTE), secret, filename)
+ File.join(secret, identifier),
+ File.join(base_dir(Store::REMOTE), secret, identifier)
]
end
diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb
index 5aa1bc7227c..b8ecfc4ee2b 100644
--- a/app/uploaders/object_storage.rb
+++ b/app/uploaders/object_storage.rb
@@ -10,6 +10,17 @@ module ObjectStorage
UnknownStoreError = Class.new(StandardError)
ObjectStorageUnavailable = Class.new(StandardError)
+ class ExclusiveLeaseTaken < StandardError
+ def initialize(lease_key)
+ @lease_key = lease_key
+ end
+
+ def message
+ *lease_key_group, _ = *@lease_key.split(":")
+ "Exclusive lease for #{lease_key_group.join(':')} is already taken."
+ end
+ end
+
TMP_UPLOAD_PATH = 'tmp/uploads'.freeze
module Store
@@ -22,14 +33,14 @@ module ObjectStorage
module RecordsUploads
extend ActiveSupport::Concern
- def prepended(base)
+ prepended do |base|
raise "#{base} must include ObjectStorage::Concern to use extensions." unless base < Concern
- base.include(RecordsUploads::Concern)
+ base.include(::RecordsUploads::Concern)
end
def retrieve_from_store!(identifier)
- paths = store_dirs.map { |store, path| File.join(path, identifier) }
+ paths = upload_paths(identifier)
unless current_upload_satisfies?(paths, model)
# the upload we already have isn't right, find the correct one
@@ -62,6 +73,15 @@ module ObjectStorage
upload.id)
end
+ def exclusive_lease_key
+ # For FileUploaders, model may have many uploaders. In that case
+ # we want to use exclusive key per upload, not per model to allow
+ # parallel migration
+ key_object = upload || model
+
+ "object_storage_migrate:#{key_object.class}:#{key_object.id}"
+ end
+
private
def current_upload_satisfies?(paths, model)
@@ -261,7 +281,7 @@ module ObjectStorage
end
def delete_migrated_file(migrated_file)
- migrated_file.delete if exists?
+ migrated_file.delete
end
def exists?
@@ -279,6 +299,13 @@ module ObjectStorage
}
end
+ # Returns all the possible paths for an upload.
+ # the `upload.path` is a lookup parameter, and it may change
+ # depending on the `store` param.
+ def upload_paths(identifier)
+ store_dirs.map { |store, path| File.join(path, identifier) }
+ end
+
def cache!(new_file = sanitized_file)
# We intercept ::UploadedFile which might be stored on remote storage
# We use that for "accelerated" uploads, where we store result on remote storage
@@ -298,6 +325,10 @@ module ObjectStorage
super
end
+ def exclusive_lease_key
+ "object_storage_migrate:#{model.class}:#{model.id}"
+ end
+
private
def schedule_background_upload?
@@ -364,17 +395,14 @@ module ObjectStorage
end
end
- def exclusive_lease_key
- "object_storage_migrate:#{model.class}:#{model.id}"
- end
-
def with_exclusive_lease
- uuid = Gitlab::ExclusiveLease.new(exclusive_lease_key, timeout: 1.hour.to_i).try_obtain
- raise 'exclusive lease already taken' unless uuid
+ lease_key = exclusive_lease_key
+ uuid = Gitlab::ExclusiveLease.new(lease_key, timeout: 1.hour.to_i).try_obtain
+ raise ExclusiveLeaseTaken.new(lease_key) unless uuid
yield uuid
ensure
- Gitlab::ExclusiveLease.cancel(exclusive_lease_key, uuid)
+ Gitlab::ExclusiveLease.cancel(lease_key, uuid)
end
#
diff --git a/app/uploaders/records_uploads.rb b/app/uploaders/records_uploads.rb
index 89c74a78835..301f4681fcd 100644
--- a/app/uploaders/records_uploads.rb
+++ b/app/uploaders/records_uploads.rb
@@ -22,7 +22,7 @@ module RecordsUploads
Upload.transaction do
uploads.where(path: upload_path).delete_all
- upload.destroy! if upload
+ upload.delete if upload
self.upload = build_upload.tap(&:save!)
end
diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb
index fd446d31092..207928b61d0 100644
--- a/app/uploaders/uploader_helper.rb
+++ b/app/uploaders/uploader_helper.rb
@@ -1,6 +1,6 @@
# Extra methods for uploader
module UploaderHelper
- IMAGE_EXT = %w[png jpg jpeg gif bmp tiff].freeze
+ IMAGE_EXT = %w[png jpg jpeg gif bmp tiff ico].freeze
# We recommend using the .mp4 format over .mov. Videos in .mov format can
# still be used but you really need to make sure they are served with the
# proper MIME type video/mp4 and not video/quicktime or your videos won't play
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
index 8648c4c75e3..6854fec582e 100644
--- a/app/validators/url_validator.rb
+++ b/app/validators/url_validator.rb
@@ -18,6 +18,13 @@
# This validator can also block urls pointing to localhost or the local network to
# protect against Server-side Request Forgery (SSRF), or check for the right port.
#
+# The available options are:
+# - protocols: Allowed protocols. Default: http and https
+# - allow_localhost: Allow urls pointing to localhost. Default: true
+# - allow_local_network: Allow urls pointing to private network addresses. Default: true
+# - ports: Allowed ports. Default: all.
+# - enforce_user: Validate user format. Default: false
+#
# Example:
# class User < ActiveRecord::Base
# validates :personal_url, url: { allow_localhost: false, allow_local_network: false}
@@ -35,7 +42,7 @@ class UrlValidator < ActiveModel::EachValidator
if value.present?
value.strip!
else
- record.errors.add(attribute, "must be a valid URL")
+ record.errors.add(attribute, 'must be a valid URL')
end
Gitlab::UrlBlocker.validate!(value, blocker_args)
@@ -51,7 +58,8 @@ class UrlValidator < ActiveModel::EachValidator
protocols: DEFAULT_PROTOCOLS,
ports: [],
allow_localhost: true,
- allow_local_network: true
+ allow_local_network: true,
+ enforce_user: false
}
end
@@ -64,7 +72,7 @@ class UrlValidator < ActiveModel::EachValidator
end
def blocker_args
- current_options.slice(:allow_localhost, :allow_local_network, :protocols, :ports).tap do |args|
+ current_options.slice(*default_options.keys).tap do |args|
if allow_setting_local_requests?
args[:allow_localhost] = args[:allow_local_network] = true
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 5e08837255f..94db374040c 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -11,13 +11,32 @@
= image_tag @appearance.header_logo_url, class: 'appearance-light-logo-preview'
- if @appearance.persisted?
%br
- = link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo"
+ = link_to 'Remove header logo', header_logos_admin_appearances_path, data: { confirm: "Header logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo"
%hr
= f.hidden_field :header_logo_cache
= f.file_field :header_logo, class: ""
.hint
Maximum file size is 1MB. Pages are optimized for a 28px tall header logo
+ %fieldset.app_logo
+ %legend
+ Favicon:
+ .form-group.row
+ = f.label :favicon, 'Favicon', class: 'col-sm-2 col-form-label'
+ .col-sm-10
+ - if @appearance.favicon?
+ = image_tag @appearance.favicon.favicon_main.url, class: 'appearance-light-logo-preview'
+ - if @appearance.persisted?
+ %br
+ = link_to 'Remove favicon', favicon_admin_appearances_path, data: { confirm: "Favicon will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo"
+ %hr
+ = f.hidden_field :favicon_cache
+ = f.file_field :favicon, class: ''
+ .hint
+ Maximum file size is 1MB. Allowed image formats are #{favicon_extension_whitelist}.
+ %br
+ The resulting favicons will be cropped to be square and scaled down to a size of 32x32 px.
+
%fieldset.sign-in
%legend
Sign in/Sign up pages:
@@ -38,7 +57,7 @@
= image_tag @appearance.logo_url, class: 'appearance-logo-preview'
- if @appearance.persisted?
%br
- = link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-sm remove-logo"
+ = link_to 'Remove logo', logo_admin_appearances_path, data: { confirm: "Logo will be removed. Are you sure?"}, method: :delete, class: "btn btn-inverted btn-remove btn-sm remove-logo"
%hr
= f.hidden_field :logo_cache
= f.file_field :logo, class: ""
diff --git a/app/views/admin/application_settings/_abuse.html.haml b/app/views/admin/application_settings/_abuse.html.haml
index 6b9b2a17dd9..91993838fc8 100644
--- a/app/views/admin/application_settings/_abuse.html.haml
+++ b/app/views/admin/application_settings/_abuse.html.haml
@@ -2,11 +2,10 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :admin_notification_email, 'Abuse reports notification email', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :admin_notification_email, class: 'form-control'
- .form-text.text-muted
- Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.
+ .form-group
+ = f.label :admin_notification_email, 'Abuse reports notification email', class: 'label-light'
+ = f.text_field :admin_notification_email, class: 'form-control'
+ .form-text.text-muted
+ Abuse reports will be sent to this address if it is set. Abuse reports are always available in the admin area.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml
index 07f9ea0865b..f40939747f4 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -2,38 +2,32 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :gravatar_enabled, class: 'form-check-input'
- = f.label :gravatar_enabled, class: 'form-check-label' do
- Gravatar enabled
- .form-group.row
- = f.label :default_projects_limit, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :default_projects_limit, class: 'form-control'
- .form-group.row
- = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :max_attachment_size, class: 'form-control'
- .form-group.row
- = f.label :session_expire_delay, 'Session duration (minutes)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :session_expire_delay, class: 'form-control'
- %span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes
- .form-group.row
- = f.label :user_oauth_applications, 'User OAuth applications', class: 'col-form-label col-sm-2'
- .col-sm-10
- .form-check
- = f.check_box :user_oauth_applications, class: 'form-check-input'
- = f.label :user_oauth_applications, class: 'form-check-label' do
- Allow users to register any application to use GitLab as an OAuth provider
- .form-group.row
- = f.label :user_default_external, 'New users set to external', class: 'col-form-label col-sm-2'
- .col-sm-10
- .form-check
- = f.check_box :user_default_external, class: 'form-check-input'
- = f.label :user_default_external, class: 'form-check-label' do
- Newly registered users will by default be external
+ .form-group
+ .form-check
+ = f.check_box :gravatar_enabled, class: 'form-check-input'
+ = f.label :gravatar_enabled, class: 'form-check-label' do
+ Gravatar enabled
+ .form-group
+ = f.label :default_projects_limit, class: 'label-light'
+ = f.number_field :default_projects_limit, class: 'form-control'
+ .form-group
+ = f.label :max_attachment_size, 'Maximum attachment size (MB)', class: 'label-light'
+ = f.number_field :max_attachment_size, class: 'form-control'
+ .form-group
+ = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light'
+ = f.number_field :session_expire_delay, class: 'form-control'
+ %span.form-text.text-muted#session_expire_delay_help_block GitLab restart is required to apply changes
+ .form-group
+ = f.label :user_oauth_applications, 'User OAuth applications', class: 'label-light'
+ .form-check
+ = f.check_box :user_oauth_applications, class: 'form-check-input'
+ = f.label :user_oauth_applications, class: 'form-check-label' do
+ Allow users to register any application to use GitLab as an OAuth provider
+ .form-group
+ = f.label :user_default_external, 'New users set to external', class: 'label-light'
+ .form-check
+ = f.check_box :user_default_external, class: 'form-check-input'
+ = f.label :user_default_external, class: 'form-check-label' do
+ Newly registered users will by default be external
= f.submit 'Save changes', class: 'btn btn-success'
diff --git a/app/views/admin/application_settings/_background_jobs.html.haml b/app/views/admin/application_settings/_background_jobs.html.haml
index fc5df02242a..fd8e695ed49 100644
--- a/app/views/admin/application_settings/_background_jobs.html.haml
+++ b/app/views/admin/application_settings/_background_jobs.html.haml
@@ -6,25 +6,22 @@
These settings require a
= link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect.
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :sidekiq_throttling_enabled, class: 'form-check-input'
- = f.label :sidekiq_throttling_enabled, class: 'form-check-label' do
- Enable Sidekiq Job Throttling
- .form-text.text-muted
- Limit the amount of resources slow running jobs are assigned.
- .form-group.row
- = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.select :sidekiq_throttling_queues, sidekiq_queue_options_for_select, { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'sidekiq_throttling_queues' }
+ .form-group
+ .form-check
+ = f.check_box :sidekiq_throttling_enabled, class: 'form-check-input'
+ = f.label :sidekiq_throttling_enabled, class: 'form-check-label' do
+ Enable Sidekiq Job Throttling
.form-text.text-muted
- Choose which queues you wish to throttle.
- .form-group.row
- = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :sidekiq_throttling_factor, class: 'form-control', min: '0.01', max: '0.99', step: '0.01'
- .form-text.text-muted
- The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.
+ Limit the amount of resources slow running jobs are assigned.
+ .form-group
+ = f.label :sidekiq_throttling_queues, 'Sidekiq queues to throttle', class: 'label-light'
+ = f.select :sidekiq_throttling_queues, sidekiq_queue_options_for_select, { include_hidden: false }, multiple: true, class: 'select2 select-wide', data: { field: 'sidekiq_throttling_queues' }
+ .form-text.text-muted
+ Choose which queues you wish to throttle.
+ .form-group
+ = f.label :sidekiq_throttling_factor, 'Throttling Factor', class: 'label-light'
+ = f.number_field :sidekiq_throttling_factor, class: 'form-control', min: '0.01', max: '0.99', step: '0.01'
+ .form-text.text-muted
+ The factor by which the queues should be throttled. A value between 0.0 and 1.0, exclusive.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index 233821818e6..7c16cafe13f 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -2,46 +2,40 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :auto_devops_enabled, class: 'form-check-input'
- = f.label :auto_devops_enabled, class: 'form-check-label' do
- Enabled Auto DevOps for projects by default
- .form-text.text-muted
- It will automatically build, test, and deploy applications based on a predefined CI/CD configuration
- = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md')
- .form-group.row
- = f.label :auto_devops_domain, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :auto_devops_domain, class: 'form-control', placeholder: 'domain.com'
- .form-text.text-muted
- = s_("AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages.")
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :shared_runners_enabled, class: 'form-check-input'
- = f.label :shared_runners_enabled, class: 'form-check-label' do
- Enable shared runners for new projects
- .form-group.row
- = f.label :shared_runners_text, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_area :shared_runners_text, class: 'form-control', rows: 4
- .form-text.text-muted Markdown enabled
- .form-group.row
- = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :max_artifacts_size, class: 'form-control'
- .form-text.text-muted
- Set the maximum file size for each job's artifacts
- = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
- .form-group.row
- = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :default_artifacts_expire_in, class: 'form-control'
- .form-text.text-muted
- Set the default expiration time for each job's artifacts.
- 0 for unlimited.
- = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
+ .form-group
+ .form-check
+ = f.check_box :auto_devops_enabled, class: 'form-check-input'
+ = f.label :auto_devops_enabled, class: 'form-check-label' do
+ Enabled Auto DevOps for projects by default
+ .form-text.text-muted
+ It will automatically build, test, and deploy applications based on a predefined CI/CD configuration
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md')
+ .form-group
+ = f.label :auto_devops_domain, class: 'label-light'
+ = f.text_field :auto_devops_domain, class: 'form-control', placeholder: 'domain.com'
+ .form-text.text-muted
+ = s_("AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages.")
+ .form-group
+ .form-check
+ = f.check_box :shared_runners_enabled, class: 'form-check-input'
+ = f.label :shared_runners_enabled, class: 'form-check-label' do
+ Enable shared runners for new projects
+ .form-group
+ = f.label :shared_runners_text, class: 'label-light'
+ = f.text_area :shared_runners_text, class: 'form-control', rows: 4
+ .form-text.text-muted Markdown enabled
+ .form-group
+ = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'label-light'
+ = f.number_field :max_artifacts_size, class: 'form-control'
+ .form-text.text-muted
+ Set the maximum file size for each job's artifacts
+ = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
+ .form-group
+ = f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'label-light'
+ = f.text_field :default_artifacts_expire_in, class: 'form-control'
+ .form-text.text-muted
+ Set the default expiration time for each job's artifacts.
+ 0 for unlimited.
+ = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_email.html.haml b/app/views/admin/application_settings/_email.html.haml
index 01be5878a60..99e44ffa741 100644
--- a/app/views/admin/application_settings/_email.html.haml
+++ b/app/views/admin/application_settings/_email.html.haml
@@ -2,25 +2,23 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :email_author_in_body, class: 'form-check-input'
- = f.label :email_author_in_body, class: 'form-check-label' do
- Include author name in notification email body
- .form-text.text-muted
- Some email servers do not support overriding the email sender name.
- Enable this option to include the name of the author of the issue,
- merge request or comment in the email body instead.
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :html_emails_enabled, class: 'form-check-input'
- = f.label :html_emails_enabled, class: 'form-check-label' do
- Enable HTML emails
- .form-text.text-muted
- By default GitLab sends emails in HTML and plain text formats so mail
- clients can choose what format to use. Disable this option if you only
- want to send emails in plain text format.
+ .form-group
+ .form-check
+ = f.check_box :email_author_in_body, class: 'form-check-input'
+ = f.label :email_author_in_body, class: 'form-check-label' do
+ Include author name in notification email body
+ .form-text.text-muted
+ Some email servers do not support overriding the email sender name.
+ Enable this option to include the name of the author of the issue,
+ merge request or comment in the email body instead.
+ .form-group
+ .form-check
+ = f.check_box :html_emails_enabled, class: 'form-check-input'
+ = f.label :html_emails_enabled, class: 'form-check-label' do
+ Enable HTML emails
+ .form-text.text-muted
+ By default GitLab sends emails in HTML and plain text formats so mail
+ clients can choose what format to use. Disable this option if you only
+ want to send emails in plain text format.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_gitaly.html.haml b/app/views/admin/application_settings/_gitaly.html.haml
index 859a1c6f45c..0b4001c0824 100644
--- a/app/views/admin/application_settings/_gitaly.html.haml
+++ b/app/views/admin/application_settings/_gitaly.html.haml
@@ -2,26 +2,23 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :gitaly_timeout_default, 'Default Timeout Period', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :gitaly_timeout_default, class: 'form-control'
- .form-text.text-muted
- Timeout for Gitaly calls from the GitLab application (in seconds). This timeout is not enforced
- for git fetch/push operations or Sidekiq jobs.
- .form-group.row
- = f.label :gitaly_timeout_fast, 'Fast Timeout Period', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :gitaly_timeout_fast, class: 'form-control'
- .form-text.text-muted
- Fast operation timeout (in seconds). Some Gitaly operations are expected to be fast.
- If they exceed this threshold, there may be a problem with a storage shard and 'failing fast'
- can help maintain the stability of the GitLab instance.
- .form-group.row
- = f.label :gitaly_timeout_medium, 'Medium Timeout Period', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :gitaly_timeout_medium, class: 'form-control'
- .form-text.text-muted
- Medium operation timeout (in seconds). This should be a value between the Fast and the Default timeout.
+ .form-group
+ = f.label :gitaly_timeout_default, 'Default Timeout Period', class: 'label-light'
+ = f.number_field :gitaly_timeout_default, class: 'form-control'
+ .form-text.text-muted
+ Timeout for Gitaly calls from the GitLab application (in seconds). This timeout is not enforced
+ for git fetch/push operations or Sidekiq jobs.
+ .form-group
+ = f.label :gitaly_timeout_fast, 'Fast Timeout Period', class: 'label-light'
+ = f.number_field :gitaly_timeout_fast, class: 'form-control'
+ .form-text.text-muted
+ Fast operation timeout (in seconds). Some Gitaly operations are expected to be fast.
+ If they exceed this threshold, there may be a problem with a storage shard and 'failing fast'
+ can help maintain the stability of the GitLab instance.
+ .form-group
+ = f.label :gitaly_timeout_medium, 'Medium Timeout Period', class: 'label-light'
+ = f.number_field :gitaly_timeout_medium, class: 'form-control'
+ .form-text.text-muted
+ Medium operation timeout (in seconds). This should be a value between the Fast and the Default timeout.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml
index 1f6c52d8b1a..1f402fcb786 100644
--- a/app/views/admin/application_settings/_help_page.html.haml
+++ b/app/views/admin/application_settings/_help_page.html.haml
@@ -2,21 +2,18 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :help_page_text, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_area :help_page_text, class: 'form-control', rows: 4
- .form-text.text-muted Markdown enabled
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :help_page_hide_commercial_content, class: 'form-check-input'
- = f.label :help_page_hide_commercial_content, class: 'form-check-label' do
- Hide marketing-related entries from help
- .form-group.row
- = f.label :help_page_support_url, 'Support page URL', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
- %span.form-text.text-muted#support_help_block Alternate support URL for help page
+ .form-group
+ = f.label :help_page_text, class: 'label-light'
+ = f.text_area :help_page_text, class: 'form-control', rows: 4
+ .form-text.text-muted Markdown enabled
+ .form-group
+ .form-check
+ = f.check_box :help_page_hide_commercial_content, class: 'form-check-input'
+ = f.label :help_page_hide_commercial_content, class: 'form-check-label' do
+ Hide marketing-related entries from help
+ .form-group
+ = f.label :help_page_support_url, 'Support page URL', class: 'label-light'
+ = f.text_field :help_page_support_url, class: 'form-control', placeholder: 'http://company.example.com/getting-help', :'aria-describedby' => 'support_help_block'
+ %span.form-text.text-muted#support_help_block Alternate support URL for help page
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_influx.html.haml b/app/views/admin/application_settings/_influx.html.haml
index b40a714ed8f..61e8e3199a9 100644
--- a/app/views/admin/application_settings/_influx.html.haml
+++ b/app/views/admin/application_settings/_influx.html.haml
@@ -8,61 +8,53 @@
= link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect.
= link_to icon('question-circle'), help_page_path('administration/monitoring/performance/introduction')
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :metrics_enabled, class: 'form-check-input'
- = f.label :metrics_enabled, class: 'form-check-label' do
- Enable InfluxDB Metrics
- .form-group.row
- = f.label :metrics_host, 'InfluxDB host', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :metrics_host, class: 'form-control', placeholder: 'influxdb.example.com'
- .form-group.row
- = f.label :metrics_port, 'InfluxDB port', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :metrics_port, class: 'form-control', placeholder: '8089'
- .form-text.text-muted
- The UDP port to use for connecting to InfluxDB. InfluxDB requires that
- your server configuration specifies a database to store data in when
- sending messages to this port, without it metrics data will not be
- saved.
- .form-group.row
- = f.label :metrics_pool_size, 'Connection pool size', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :metrics_pool_size, class: 'form-control'
- .form-text.text-muted
- The amount of InfluxDB connections to open. Connections are opened
- lazily. Users using multi-threaded application servers should ensure
- enough connections are available (at minimum the amount of application
- server threads).
- .form-group.row
- = f.label :metrics_timeout, 'Connection timeout', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :metrics_timeout, class: 'form-control'
- .form-text.text-muted
- The amount of seconds after which an InfluxDB connection will time
- out.
- .form-group.row
- = f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :metrics_method_call_threshold, class: 'form-control'
- .form-text.text-muted
- A method call is only tracked when it takes longer to complete than
- the given amount of milliseconds.
- .form-group.row
- = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :metrics_sample_interval, class: 'form-control'
- .form-text.text-muted
- The sampling interval in seconds. Sampled data includes memory usage,
- retained Ruby objects, file descriptors and so on.
- .form-group.row
- = f.label :metrics_packet_size, 'Metrics per packet', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :metrics_packet_size, class: 'form-control'
- .form-text.text-muted
- The amount of points to store in a single UDP packet. More points
- results in fewer but larger UDP packets being sent.
+ .form-group
+ .form-check
+ = f.check_box :metrics_enabled, class: 'form-check-input'
+ = f.label :metrics_enabled, class: 'form-check-label' do
+ Enable InfluxDB Metrics
+ .form-group
+ = f.label :metrics_host, 'InfluxDB host', class: 'label-light'
+ = f.text_field :metrics_host, class: 'form-control', placeholder: 'influxdb.example.com'
+ .form-group
+ = f.label :metrics_port, 'InfluxDB port', class: 'label-light'
+ = f.text_field :metrics_port, class: 'form-control', placeholder: '8089'
+ .form-text.text-muted
+ The UDP port to use for connecting to InfluxDB. InfluxDB requires that
+ your server configuration specifies a database to store data in when
+ sending messages to this port, without it metrics data will not be
+ saved.
+ .form-group
+ = f.label :metrics_pool_size, 'Connection pool size', class: 'label-light'
+ = f.number_field :metrics_pool_size, class: 'form-control'
+ .form-text.text-muted
+ The amount of InfluxDB connections to open. Connections are opened
+ lazily. Users using multi-threaded application servers should ensure
+ enough connections are available (at minimum the amount of application
+ server threads).
+ .form-group
+ = f.label :metrics_timeout, 'Connection timeout', class: 'label-light'
+ = f.number_field :metrics_timeout, class: 'form-control'
+ .form-text.text-muted
+ The amount of seconds after which an InfluxDB connection will time
+ out.
+ .form-group
+ = f.label :metrics_method_call_threshold, 'Method Call Threshold (ms)', class: 'label-light'
+ = f.number_field :metrics_method_call_threshold, class: 'form-control'
+ .form-text.text-muted
+ A method call is only tracked when it takes longer to complete than
+ the given amount of milliseconds.
+ .form-group
+ = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'label-light'
+ = f.number_field :metrics_sample_interval, class: 'form-control'
+ .form-text.text-muted
+ The sampling interval in seconds. Sampled data includes memory usage,
+ retained Ruby objects, file descriptors and so on.
+ .form-group
+ = f.label :metrics_packet_size, 'Metrics per packet', class: 'label-light'
+ = f.number_field :metrics_packet_size, class: 'form-control'
+ .form-text.text-muted
+ The amount of points to store in a single UDP packet. More points
+ results in fewer but larger UDP packets being sent.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_ip_limits.html.haml b/app/views/admin/application_settings/_ip_limits.html.haml
index 320dd52ffc2..73d570a5fee 100644
--- a/app/views/admin/application_settings/_ip_limits.html.haml
+++ b/app/views/admin/application_settings/_ip_limits.html.haml
@@ -2,53 +2,44 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :throttle_unauthenticated_enabled, class: 'form-check-input'
- = f.label :throttle_unauthenticated_enabled, class: 'form-check-label' do
- Enable unauthenticated request rate limit
- %span.form-text.text-muted
- Helps reduce request volume (e.g. from crawlers or abusive bots)
- .form-group.row
- = f.label :throttle_unauthenticated_requests_per_period, 'Max requests per period per IP', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :throttle_unauthenticated_requests_per_period, class: 'form-control'
- .form-group.row
- = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :throttle_unauthenticated_period_in_seconds, class: 'form-control'
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :throttle_authenticated_api_enabled, class: 'form-check-input'
- = f.label :throttle_authenticated_api_enabled, class: 'form-check-label' do
- Enable authenticated API request rate limit
- %span.form-text.text-muted
- Helps reduce request volume (e.g. from crawlers or abusive bots)
- .form-group.row
- = f.label :throttle_authenticated_api_requests_per_period, 'Max requests per period per user', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :throttle_authenticated_api_requests_per_period, class: 'form-control'
- .form-group.row
- = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :throttle_authenticated_api_period_in_seconds, class: 'form-control'
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :throttle_authenticated_web_enabled, class: 'form-check-input'
- = f.label :throttle_authenticated_web_enabled, class: 'form-check-label' do
- Enable authenticated web request rate limit
- %span.form-text.text-muted
- Helps reduce request volume (e.g. from crawlers or abusive bots)
- .form-group.row
- = f.label :throttle_authenticated_web_requests_per_period, 'Max requests per period per user', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :throttle_authenticated_web_requests_per_period, class: 'form-control'
- .form-group.row
- = f.label :throttle_authenticated_web_period_in_seconds, 'Rate limit period in seconds', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control'
+ .form-group
+ .form-check
+ = f.check_box :throttle_unauthenticated_enabled, class: 'form-check-input'
+ = f.label :throttle_unauthenticated_enabled, class: 'form-check-label' do
+ Enable unauthenticated request rate limit
+ %span.form-text.text-muted
+ Helps reduce request volume (e.g. from crawlers or abusive bots)
+ .form-group
+ = f.label :throttle_unauthenticated_requests_per_period, 'Max requests per period per IP', class: 'label-light'
+ = f.number_field :throttle_unauthenticated_requests_per_period, class: 'form-control'
+ .form-group
+ = f.label :throttle_unauthenticated_period_in_seconds, 'Rate limit period in seconds', class: 'label-light'
+ = f.number_field :throttle_unauthenticated_period_in_seconds, class: 'form-control'
+ .form-group
+ .form-check
+ = f.check_box :throttle_authenticated_api_enabled, class: 'form-check-input'
+ = f.label :throttle_authenticated_api_enabled, class: 'form-check-label' do
+ Enable authenticated API request rate limit
+ %span.form-text.text-muted
+ Helps reduce request volume (e.g. from crawlers or abusive bots)
+ .form-group
+ = f.label :throttle_authenticated_api_requests_per_period, 'Max requests per period per user', class: 'label-light'
+ = f.number_field :throttle_authenticated_api_requests_per_period, class: 'form-control'
+ .form-group
+ = f.label :throttle_authenticated_api_period_in_seconds, 'Rate limit period in seconds', class: 'label-light'
+ = f.number_field :throttle_authenticated_api_period_in_seconds, class: 'form-control'
+ .form-group
+ .form-check
+ = f.check_box :throttle_authenticated_web_enabled, class: 'form-check-input'
+ = f.label :throttle_authenticated_web_enabled, class: 'form-check-label' do
+ Enable authenticated web request rate limit
+ %span.form-text.text-muted
+ Helps reduce request volume (e.g. from crawlers or abusive bots)
+ .form-group
+ = f.label :throttle_authenticated_web_requests_per_period, 'Max requests per period per user', class: 'label-light'
+ = f.number_field :throttle_authenticated_web_requests_per_period, class: 'form-control'
+ .form-group
+ = f.label :throttle_authenticated_web_period_in_seconds, 'Rate limit period in seconds', class: 'label-light'
+ = f.number_field :throttle_authenticated_web_period_in_seconds, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_koding.html.haml b/app/views/admin/application_settings/_koding.html.haml
index 341c7641fcc..ae60f68f5fe 100644
--- a/app/views/admin/application_settings/_koding.html.haml
+++ b/app/views/admin/application_settings/_koding.html.haml
@@ -2,23 +2,21 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :koding_enabled, class: 'form-check-input'
- = f.label :koding_enabled, class: 'form-check-label' do
- Enable Koding
- .form-text.text-muted
- Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again.
- .form-group.row
- = f.label :koding_url, 'Koding URL', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :koding_url, class: 'form-control', placeholder: 'http://gitlab.your-koding-instance.com:8090'
- .form-text.text-muted
- Koding has integration enabled out of the box for the
- %strong gitlab
- team, and you need to provide that team's URL here. Learn more in the
- = succeed "." do
- = link_to "Koding administration documentation", help_page_path("administration/integration/koding")
+ .form-group
+ .form-check
+ = f.check_box :koding_enabled, class: 'form-check-input'
+ = f.label :koding_enabled, class: 'form-check-label' do
+ Enable Koding
+ .form-text.text-muted
+ Koding integration has been deprecated since GitLab 10.0. If you disable your Koding integration, you will not be able to enable it again.
+ .form-group
+ = f.label :koding_url, 'Koding URL', class: 'label-light'
+ = f.text_field :koding_url, class: 'form-control', placeholder: 'http://gitlab.your-koding-instance.com:8090'
+ .form-text.text-muted
+ Koding has integration enabled out of the box for the
+ %strong gitlab
+ team, and you need to provide that team's URL here. Learn more in the
+ = succeed "." do
+ = link_to "Koding administration documentation", help_page_path("administration/integration/koding")
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_logging.html.haml b/app/views/admin/application_settings/_logging.html.haml
index f5c1e126c70..a6e549cd1f0 100644
--- a/app/views/admin/application_settings/_logging.html.haml
+++ b/app/views/admin/application_settings/_logging.html.haml
@@ -2,35 +2,31 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :sentry_enabled, class: 'form-check-input'
- = f.label :sentry_enabled, class: 'form-check-label' do
- Enable Sentry
- .form-text.text-muted
- %p This setting requires a restart to take effect.
- Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here:
- %a{ href: 'https://getsentry.com', target: '_blank', rel: 'noopener noreferrer' } https://getsentry.com
+ .form-group
+ .form-check
+ = f.check_box :sentry_enabled, class: 'form-check-input'
+ = f.label :sentry_enabled, class: 'form-check-label' do
+ Enable Sentry
+ .form-text.text-muted
+ %p This setting requires a restart to take effect.
+ Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here:
+ %a{ href: 'https://getsentry.com', target: '_blank', rel: 'noopener noreferrer' } https://getsentry.com
- .form-group.row
- = f.label :sentry_dsn, 'Sentry DSN', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :sentry_dsn, class: 'form-control'
+ .form-group
+ = f.label :sentry_dsn, 'Sentry DSN', class: 'label-light'
+ = f.text_field :sentry_dsn, class: 'form-control'
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :clientside_sentry_enabled, class: 'form-check-input'
- = f.label :clientside_sentry_enabled, class: 'form-check-label' do
- Enable Clientside Sentry
- .form-text.text-muted
- Sentry can also be used for reporting and logging clientside exceptions.
- %a{ href: 'https://sentry.io/for/javascript/', target: '_blank', rel: 'noopener noreferrer' } https://sentry.io/for/javascript/
+ .form-group
+ .form-check
+ = f.check_box :clientside_sentry_enabled, class: 'form-check-input'
+ = f.label :clientside_sentry_enabled, class: 'form-check-label' do
+ Enable Clientside Sentry
+ .form-text.text-muted
+ Sentry can also be used for reporting and logging clientside exceptions.
+ %a{ href: 'https://sentry.io/for/javascript/', target: '_blank', rel: 'noopener noreferrer' } https://sentry.io/for/javascript/
- .form-group.row
- = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :clientside_sentry_dsn, class: 'form-control'
+ .form-group
+ = f.label :clientside_sentry_dsn, 'Clientside Sentry DSN', class: 'label-light'
+ = f.text_field :clientside_sentry_dsn, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_outbound.html.haml b/app/views/admin/application_settings/_outbound.html.haml
index 5dadb7b814b..e046242bee0 100644
--- a/app/views/admin/application_settings/_outbound.html.haml
+++ b/app/views/admin/application_settings/_outbound.html.haml
@@ -2,11 +2,10 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :allow_local_requests_from_hooks_and_services, class: 'form-check-input'
- = f.label :allow_local_requests_from_hooks_and_services, class: 'form-check-label' do
- Allow requests to the local network from hooks and services
+ .form-group
+ .form-check
+ = f.check_box :allow_local_requests_from_hooks_and_services, class: 'form-check-input'
+ = f.label :allow_local_requests_from_hooks_and_services, class: 'form-check-label' do
+ Allow requests to the local network from hooks and services
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_pages.html.haml b/app/views/admin/application_settings/_pages.html.haml
index f1889c3105f..f168ec62ffd 100644
--- a/app/views/admin/application_settings/_pages.html.haml
+++ b/app/views/admin/application_settings/_pages.html.haml
@@ -2,21 +2,19 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :max_pages_size, class: 'form-control'
- .form-text.text-muted 0 for unlimited
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :pages_domain_verification_enabled, class: 'form-check-input'
- = f.label :pages_domain_verification_enabled, class: 'form-check-label' do
- Require users to prove ownership of custom domains
- .form-text.text-muted
- 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')
+ .form-group
+ = f.label :max_pages_size, 'Maximum size of pages (MB)', class: 'label-light'
+ = f.number_field :max_pages_size, class: 'form-control'
+ .form-text.text-muted 0 for unlimited
+ .form-group
+ .form-check
+ = f.check_box :pages_domain_verification_enabled, class: 'form-check-input'
+ = f.label :pages_domain_verification_enabled, class: 'form-check-label' do
+ Require users to prove ownership of custom domains
+ .form-text.text-muted
+ 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')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_performance.html.haml b/app/views/admin/application_settings/_performance.html.haml
index 57c22ce563f..ffa25af77ed 100644
--- a/app/views/admin/application_settings/_performance.html.haml
+++ b/app/views/admin/application_settings/_performance.html.haml
@@ -2,18 +2,17 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :authorized_keys_enabled, class: 'form-check-input'
- = f.label :authorized_keys_enabled, class: 'form-check-label' do
- Write to "authorized_keys" file
- .form-text.text-muted
- By default, we write to the "authorized_keys" file to support Git
- over SSH without additional configuration. GitLab can be optimized
- to authenticate SSH keys via the database file. Only uncheck this
- if you have configured your OpenSSH server to use the
- AuthorizedKeysCommand. Click on the help icon for more details.
- = link_to icon('question-circle'), help_page_path('administration/operations/fast_ssh_key_lookup')
+ .form-group
+ .form-check
+ = f.check_box :authorized_keys_enabled, class: 'form-check-input'
+ = f.label :authorized_keys_enabled, class: 'form-check-label' do
+ Write to "authorized_keys" file
+ .form-text.text-muted
+ By default, we write to the "authorized_keys" file to support Git
+ over SSH without additional configuration. GitLab can be optimized
+ to authenticate SSH keys via the database file. Only uncheck this
+ if you have configured your OpenSSH server to use the
+ AuthorizedKeysCommand. Click on the help icon for more details.
+ = link_to icon('question-circle'), help_page_path('administration/operations/fast_ssh_key_lookup')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_performance_bar.html.haml b/app/views/admin/application_settings/_performance_bar.html.haml
index ed4de2234f7..ddbfcc6b77b 100644
--- a/app/views/admin/application_settings/_performance_bar.html.haml
+++ b/app/views/admin/application_settings/_performance_bar.html.haml
@@ -2,15 +2,13 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :performance_bar_enabled, class: 'form-check-input'
- = f.label :performance_bar_enabled, class: 'form-check-label' do
- Enable the Performance Bar
- .form-group.row
- = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :performance_bar_allowed_group_path, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path
+ .form-group
+ .form-check
+ = f.check_box :performance_bar_enabled, class: 'form-check-input'
+ = f.label :performance_bar_enabled, class: 'form-check-label' do
+ Enable the Performance Bar
+ .form-group
+ = f.label :performance_bar_allowed_group_path, 'Allowed group', class: 'label-light'
+ = f.text_field :performance_bar_allowed_group_path, class: 'form-control', placeholder: 'my-org/my-group', value: @application_setting.performance_bar_allowed_group&.full_path
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_plantuml.html.haml b/app/views/admin/application_settings/_plantuml.html.haml
index e0dc058762e..259f18b3b96 100644
--- a/app/views/admin/application_settings/_plantuml.html.haml
+++ b/app/views/admin/application_settings/_plantuml.html.haml
@@ -2,19 +2,17 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :plantuml_enabled, class: 'form-check-input'
- = f.label :plantuml_enabled, class: 'form-check-label' do
- Enable PlantUML
- .form-group.row
- = f.label :plantuml_url, 'PlantUML URL', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://gitlab.your-plantuml-instance.com:8080'
- .form-text.text-muted
- Allow rendering of
- = link_to "PlantUML", "http://plantuml.com"
- diagrams in Asciidoc documents using an external PlantUML service.
+ .form-group
+ .form-check
+ = f.check_box :plantuml_enabled, class: 'form-check-input'
+ = f.label :plantuml_enabled, class: 'form-check-label' do
+ Enable PlantUML
+ .form-group
+ = f.label :plantuml_url, 'PlantUML URL', class: 'label-light'
+ = f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://gitlab.your-plantuml-instance.com:8080'
+ .form-text.text-muted
+ Allow rendering of
+ = link_to "PlantUML", "http://plantuml.com"
+ diagrams in Asciidoc documents using an external PlantUML service.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_prometheus.html.haml b/app/views/admin/application_settings/_prometheus.html.haml
index d3c3656e96a..ad92b18b2c9 100644
--- a/app/views/admin/application_settings/_prometheus.html.haml
+++ b/app/views/admin/application_settings/_prometheus.html.haml
@@ -11,18 +11,17 @@
= link_to 'restart', help_page_path('administration/restart_gitlab')
to take effect.
= link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/index')
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :prometheus_metrics_enabled, class: 'form-check-input'
- = f.label :prometheus_metrics_enabled, class: 'form-check-label' do
- Enable Prometheus Metrics
- - unless Gitlab::Metrics.metrics_folder_present?
- .form-text.text-muted
- %strong.cred WARNING:
- Environment variable
- %code prometheus_multiproc_dir
- does not exist or is not pointing to a valid directory.
- = link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory')
+ .form-group
+ .form-check
+ = f.check_box :prometheus_metrics_enabled, class: 'form-check-input'
+ = f.label :prometheus_metrics_enabled, class: 'form-check-label' do
+ Enable Prometheus Metrics
+ - unless Gitlab::Metrics.metrics_folder_present?
+ .form-text.text-muted
+ %strong.cred WARNING:
+ Environment variable
+ %code prometheus_multiproc_dir
+ does not exist or is not pointing to a valid directory.
+ = link_to icon('question-circle'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_realtime.html.haml b/app/views/admin/application_settings/_realtime.html.haml
index 63a592cc2fd..120cf4909b2 100644
--- a/app/views/admin/application_settings/_realtime.html.haml
+++ b/app/views/admin/application_settings/_realtime.html.haml
@@ -2,18 +2,17 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :polling_interval_multiplier, class: 'form-control'
- .form-text.text-muted
- Change this value to influence how frequently the GitLab UI polls for updates.
- If you set the value to 2 all polling intervals are multiplied
- by 2, which means that polling happens half as frequently.
- The multiplier can also have a decimal value.
- The default value (1) is a reasonable choice for the majority of GitLab
- installations. Set to 0 to completely disable polling.
- = link_to icon('question-circle'), help_page_path('administration/polling')
+ .form-group
+ = f.label :polling_interval_multiplier, 'Polling interval multiplier', class: 'label-light'
+ = f.text_field :polling_interval_multiplier, class: 'form-control'
+ .form-text.text-muted
+ Change this value to influence how frequently the GitLab UI polls for updates.
+ If you set the value to 2 all polling intervals are multiplied
+ by 2, which means that polling happens half as frequently.
+ The multiplier can also have a decimal value.
+ The default value (1) is a reasonable choice for the majority of GitLab
+ installations. Set to 0 to completely disable polling.
+ = link_to icon('question-circle'), help_page_path('administration/polling')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_registry.html.haml b/app/views/admin/application_settings/_registry.html.haml
index 8524cbfe4d9..beac70482e5 100644
--- a/app/views/admin/application_settings/_registry.html.haml
+++ b/app/views/admin/application_settings/_registry.html.haml
@@ -2,9 +2,8 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :container_registry_token_expire_delay, class: 'form-control'
+ .form-group
+ = f.label :container_registry_token_expire_delay, 'Authorization token duration (minutes)', class: 'label-light'
+ = f.number_field :container_registry_token_expire_delay, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_repository_check.html.haml b/app/views/admin/application_settings/_repository_check.html.haml
index 1311f17ecda..57facc380eb 100644
--- a/app/views/admin/application_settings/_repository_check.html.haml
+++ b/app/views/admin/application_settings/_repository_check.html.haml
@@ -4,59 +4,53 @@
%fieldset
.sub-section
%h4 Repository checks
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :repository_checks_enabled, class: 'form-check-input'
- = f.label :repository_checks_enabled, class: 'form-check-label' do
- Enable Repository Checks
- .form-text.text-muted
- GitLab will periodically run
- %a{ href: 'https://git-scm.com/docs/git-fsck', target: 'blank' } 'git fsck'
- in all project and wiki repositories to look for silent disk corruption issues.
- .form-group.row
- .offset-sm-2.col-sm-10
- = link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove"
+ .form-group
+ .form-check
+ = f.check_box :repository_checks_enabled, class: 'form-check-input'
+ = f.label :repository_checks_enabled, class: 'form-check-label' do
+ Enable Repository Checks
.form-text.text-muted
- If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.
+ GitLab will periodically run
+ %a{ href: 'https://git-scm.com/docs/git-fsck', target: 'blank' } 'git fsck'
+ in all project and wiki repositories to look for silent disk corruption issues.
+ .form-group
+ = link_to 'Clear all repository checks', clear_repository_check_states_admin_application_settings_path, data: { confirm: 'This will clear repository check states for ALL projects in the database. This cannot be undone. Are you sure?' }, method: :put, class: "btn btn-sm btn-remove"
+ .form-text.text-muted
+ If you got a lot of false alarms from repository checks you can choose to clear all repository check information from the database.
.sub-section
%h4 Housekeeping
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :housekeeping_enabled, class: 'form-check-input'
- = f.label :housekeeping_enabled, class: 'form-check-label' do
- Enable automatic repository housekeeping (git repack, git gc)
- .form-text.text-muted
- If you keep automatic housekeeping disabled for a long time Git
- repository access on your GitLab server will become slower and your
- repositories will use more disk space. We recommend to always leave
- this enabled.
- .form-check
- = f.check_box :housekeeping_bitmaps_enabled, class: 'form-check-input'
- = f.label :housekeeping_bitmaps_enabled, class: 'form-check-label' do
- Enable Git pack file bitmap creation
- .form-text.text-muted
- Creating pack file bitmaps makes housekeeping take a little longer but
- bitmaps should accelerate 'git clone' performance.
- .form-group.row
- = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :housekeeping_incremental_repack_period, class: 'form-control'
+ .form-group
+ .form-check
+ = f.check_box :housekeeping_enabled, class: 'form-check-input'
+ = f.label :housekeeping_enabled, class: 'form-check-label' do
+ Enable automatic repository housekeeping (git repack, git gc)
.form-text.text-muted
- Number of Git pushes after which an incremental 'git repack' is run.
- .form-group.row
- = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :housekeeping_full_repack_period, class: 'form-control'
+ If you keep automatic housekeeping disabled for a long time Git
+ repository access on your GitLab server will become slower and your
+ repositories will use more disk space. We recommend to always leave
+ this enabled.
+ .form-check
+ = f.check_box :housekeeping_bitmaps_enabled, class: 'form-check-input'
+ = f.label :housekeeping_bitmaps_enabled, class: 'form-check-label' do
+ Enable Git pack file bitmap creation
.form-text.text-muted
- Number of Git pushes after which a full 'git repack' is run.
- .form-group.row
- = f.label :housekeeping_gc_period, 'Git GC period', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :housekeeping_gc_period, class: 'form-control'
- .form-text.text-muted
- Number of Git pushes after which 'git gc' is run.
+ Creating pack file bitmaps makes housekeeping take a little longer but
+ bitmaps should accelerate 'git clone' performance.
+ .form-group
+ = f.label :housekeeping_incremental_repack_period, 'Incremental repack period', class: 'label-light'
+ = f.number_field :housekeeping_incremental_repack_period, class: 'form-control'
+ .form-text.text-muted
+ Number of Git pushes after which an incremental 'git repack' is run.
+ .form-group
+ = f.label :housekeeping_full_repack_period, 'Full repack period', class: 'label-light'
+ = f.number_field :housekeeping_full_repack_period, class: 'form-control'
+ .form-text.text-muted
+ Number of Git pushes after which a full 'git repack' is run.
+ .form-group
+ = f.label :housekeeping_gc_period, 'Git GC period', class: 'label-light'
+ = f.number_field :housekeeping_gc_period, class: 'form-control'
+ .form-text.text-muted
+ Number of Git pushes after which 'git gc' is run.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_repository_mirrors_form.html.haml b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
index 187c6c28bb1..beeb5169361 100644
--- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml
+++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
@@ -2,15 +2,14 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :mirror_available, 'Enable mirror configuration', class: 'control-label col-sm-4'
- .col-sm-8
- .form-check
- = f.check_box :mirror_available, class: 'form-check-input'
- = f.label :mirror_available, class: 'form-check-label' do
- Allow mirrors to be setup for projects
- %span.form-text.text-muted
- If disabled, only admins will be able to setup mirrors in projects.
- = link_to icon('question-circle'), help_page_path('workflow/repository_mirroring')
+ .form-group
+ = f.label :mirror_available, 'Enable mirror configuration', class: 'label-light'
+ .form-check
+ = f.check_box :mirror_available, class: 'form-check-input'
+ = f.label :mirror_available, class: 'form-check-label' do
+ Allow mirrors to be setup for projects
+ %span.form-text.text-muted
+ If disabled, only admins will be able to setup mirrors in projects.
+ = link_to icon('question-circle'), help_page_path('workflow/repository_mirroring')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_repository_storage.html.haml b/app/views/admin/application_settings/_repository_storage.html.haml
index 89d2c114b22..5a303666353 100644
--- a/app/views/admin/application_settings/_repository_storage.html.haml
+++ b/app/views/admin/application_settings/_repository_storage.html.haml
@@ -3,56 +3,49 @@
%fieldset
.sub-section
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :hashed_storage_enabled, class: 'form-check-input'
- = f.label :hashed_storage_enabled, class: 'form-check-label' do
- Create new projects using hashed storage paths
- .form-text.text-muted
- Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
- repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance.
- %em (EXPERIMENTAL)
- .form-group.row
- = f.label :repository_storages, 'Storage paths for new projects', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.select :repository_storages, repository_storages_options_for_select(@application_setting.repository_storages),
- {include_hidden: false}, multiple: true, class: 'form-control'
+ .form-group
+ .form-check
+ = f.check_box :hashed_storage_enabled, class: 'form-check-input'
+ = f.label :hashed_storage_enabled, class: 'form-check-label' do
+ Create new projects using hashed storage paths
.form-text.text-muted
- Manage repository storage paths. Learn more in the
- = succeed "." do
- = link_to "repository storages documentation", help_page_path("administration/repository_storage_paths")
+ Enable immutable, hash-based paths and repository names to store repositories on disk. This prevents
+ repositories from having to be moved or renamed when the Project URL changes and may improve disk I/O performance.
+ %em (EXPERIMENTAL)
+ .form-group
+ = f.label :repository_storages, 'Storage paths for new projects', class: 'label-light'
+ = f.select :repository_storages, repository_storages_options_for_select(@application_setting.repository_storages),
+ {include_hidden: false}, multiple: true, class: 'form-control'
+ .form-text.text-muted
+ Manage repository storage paths. Learn more in the
+ = succeed "." do
+ = link_to "repository storages documentation", help_page_path("administration/repository_storage_paths")
.sub-section
%h4 Circuit breaker
- .form-group.row
- = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :circuitbreaker_check_interval, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_check_interval_help_text
- .form-group.row
- = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :circuitbreaker_access_retries, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_access_retries_help_text
- .form-group.row
- = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :circuitbreaker_storage_timeout, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_storage_timeout_help_text
- .form-group.row
- = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :circuitbreaker_failure_count_threshold, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_failure_count_help_text
- .form-group.row
- = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :circuitbreaker_failure_reset_time, class: 'form-control'
- .form-text.text-muted
- = circuitbreaker_failure_reset_time_help_text
+ .form-group
+ = f.label :circuitbreaker_check_interval, _('Check interval'), class: 'label-light'
+ = f.number_field :circuitbreaker_check_interval, class: 'form-control'
+ .form-text.text-muted
+ = circuitbreaker_check_interval_help_text
+ .form-group
+ = f.label :circuitbreaker_access_retries, _('Number of access attempts'), class: 'label-light'
+ = f.number_field :circuitbreaker_access_retries, class: 'form-control'
+ .form-text.text-muted
+ = circuitbreaker_access_retries_help_text
+ .form-group
+ = f.label :circuitbreaker_storage_timeout, _('Seconds to wait for a storage access attempt'), class: 'label-light'
+ = f.number_field :circuitbreaker_storage_timeout, class: 'form-control'
+ .form-text.text-muted
+ = circuitbreaker_storage_timeout_help_text
+ .form-group
+ = f.label :circuitbreaker_failure_count_threshold, _('Maximum git storage failures'), class: 'label-light'
+ = f.number_field :circuitbreaker_failure_count_threshold, class: 'form-control'
+ .form-text.text-muted
+ = circuitbreaker_failure_count_help_text
+ .form-group
+ = f.label :circuitbreaker_failure_reset_time, _('Seconds before reseting failure information'), class: 'label-light'
+ = f.number_field :circuitbreaker_failure_reset_time, class: 'form-control'
+ .form-text.text-muted
+ = circuitbreaker_failure_reset_time_help_text
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_signin.html.haml b/app/views/admin/application_settings/_signin.html.haml
index 2ba26158162..69d1a43c511 100644
--- a/app/views/admin/application_settings/_signin.html.haml
+++ b/app/views/admin/application_settings/_signin.html.haml
@@ -2,59 +2,51 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :password_authentication_enabled_for_web, class: 'form-check-input'
- = f.label :password_authentication_enabled_for_web, class: 'form-check-label' do
- Password authentication enabled for web interface
- .form-text.text-muted
- When disabled, an external authentication provider must be used.
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :password_authentication_enabled_for_git, class: 'form-check-input'
- = f.label :password_authentication_enabled_for_git, class: 'form-check-label' do
- Password authentication enabled for Git over HTTP(S)
- .form-text.text-muted
- When disabled, a Personal Access Token
- - if Gitlab::Auth::LDAP::Config.enabled?
- or LDAP password
- must be used to authenticate.
+ .form-group
+ .form-check
+ = f.check_box :password_authentication_enabled_for_web, class: 'form-check-input'
+ = f.label :password_authentication_enabled_for_web, class: 'form-check-label' do
+ Password authentication enabled for web interface
+ .form-text.text-muted
+ When disabled, an external authentication provider must be used.
+ .form-group
+ .form-check
+ = f.check_box :password_authentication_enabled_for_git, class: 'form-check-input'
+ = f.label :password_authentication_enabled_for_git, class: 'form-check-label' do
+ Password authentication enabled for Git over HTTP(S)
+ .form-text.text-muted
+ When disabled, a Personal Access Token
+ - if Gitlab::Auth::LDAP::Config.enabled?
+ or LDAP password
+ must be used to authenticate.
- if omniauth_enabled? && button_based_providers.any?
- .form-group.row
- = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'col-form-label col-sm-2'
+ .form-group
+ = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth sign-in sources', class: 'label-light'
= hidden_field_tag 'application_setting[enabled_oauth_sign_in_sources][]'
- .col-sm-10
- .btn-group{ data: { toggle: 'buttons' } }
- - oauth_providers_checkboxes.each do |source|
- = source
- .form-group.row
- = f.label :two_factor_authentication, 'Two-factor authentication', class: 'col-form-label col-sm-2'
- .col-sm-10
- .form-check
- = f.check_box :require_two_factor_authentication, class: 'form-check-input'
- = f.label :require_two_factor_authentication, class: 'form-check-label' do
- Require all users to setup Two-factor authentication
- .form-group.row
- = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
- .form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
- .form-group.row
- = f.label :home_page_url, 'Home page URL', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
- %span.form-text.text-muted#home_help_block We will redirect non-logged in users to this page
- .form-group.row
- = f.label :after_sign_out_path, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block'
- %span.form-text.text-muted#after_sign_out_path_help_block We will redirect users to this page after they sign out
- .form-group.row
- = f.label :sign_in_text, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_area :sign_in_text, class: 'form-control', rows: 4
- .form-text.text-muted Markdown enabled
+ .btn-group{ data: { toggle: 'buttons' } }
+ - oauth_providers_checkboxes.each do |source|
+ = source
+ .form-group
+ = f.label :two_factor_authentication, 'Two-factor authentication', class: 'label-light'
+ .form-check
+ = f.check_box :require_two_factor_authentication, class: 'form-check-input'
+ = f.label :require_two_factor_authentication, class: 'form-check-label' do
+ Require all users to setup Two-factor authentication
+ .form-group
+ = f.label :two_factor_authentication, 'Two-factor grace period (hours)', class: 'label-light'
+ = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0'
+ .form-text.text-muted Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication
+ .form-group
+ = f.label :home_page_url, 'Home page URL', class: 'label-light'
+ = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block'
+ %span.form-text.text-muted#home_help_block We will redirect non-logged in users to this page
+ .form-group
+ = f.label :after_sign_out_path, class: 'label-light'
+ = f.text_field :after_sign_out_path, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'after_sign_out_path_help_block'
+ %span.form-text.text-muted#after_sign_out_path_help_block We will redirect users to this page after they sign out
+ .form-group
+ = f.label :sign_in_text, class: 'label-light'
+ = f.text_area :sign_in_text, class: 'form-control', rows: 4
+ .form-text.text-muted Markdown enabled
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_signup.html.haml b/app/views/admin/application_settings/_signup.html.haml
index 279f96389e9..b9ba9128cc9 100644
--- a/app/views/admin/application_settings/_signup.html.haml
+++ b/app/views/admin/application_settings/_signup.html.haml
@@ -2,57 +2,49 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :signup_enabled, class: 'form-check-input'
- = f.label :signup_enabled, class: 'form-check-label' do
- Sign-up enabled
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :send_user_confirmation_email, class: 'form-check-input'
- = f.label :send_user_confirmation_email, class: 'form-check-label' do
- Send confirmation email on sign-up
- .form-group.row
- = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
- .form-text.text-muted ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
- .form-group.row
- = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'col-form-label col-sm-2'
- .col-sm-10
- .form-check
- = f.check_box :domain_blacklist_enabled, class: 'form-check-input'
- = f.label :domain_blacklist_enabled, class: 'form-check-label' do
- Enable domain blacklist for sign ups
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = radio_button_tag :blacklist_type, :file, class: 'form-check-input'
- = label_tag :blacklist_type_file, class: 'form-check-label' do
- .option-title
- Upload blacklist file
- .form-check
- = radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank?, class: 'form-check-input'
- = label_tag :blacklist_type_raw, class: 'form-check-label' do
- .option-title
- Enter blacklist manually
- .form-group.row.blacklist-file
- = f.label :domain_blacklist_file, 'Blacklist file', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf'
- .form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
- .form-group.row.blacklist-raw
- = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
- .form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
+ .form-group
+ .form-check
+ = f.check_box :signup_enabled, class: 'form-check-input'
+ = f.label :signup_enabled, class: 'form-check-label' do
+ Sign-up enabled
+ .form-group
+ .form-check
+ = f.check_box :send_user_confirmation_email, class: 'form-check-input'
+ = f.label :send_user_confirmation_email, class: 'form-check-label' do
+ Send confirmation email on sign-up
+ .form-group
+ = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'label-light'
+ = f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
+ .form-text.text-muted ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
+ .form-group
+ = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'label-light'
+ .form-check
+ = f.check_box :domain_blacklist_enabled, class: 'form-check-input'
+ = f.label :domain_blacklist_enabled, class: 'form-check-label' do
+ Enable domain blacklist for sign ups
+ .form-group
+ .form-check
+ = radio_button_tag :blacklist_type, :file, false, class: 'form-check-input'
+ = label_tag :blacklist_type_file, class: 'form-check-label' do
+ .option-title
+ Upload blacklist file
+ .form-check
+ = radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank?, class: 'form-check-input'
+ = label_tag :blacklist_type_raw, class: 'form-check-label' do
+ .option-title
+ Enter blacklist manually
+ .form-group.blacklist-file
+ = f.label :domain_blacklist_file, 'Blacklist file', class: 'label-light'
+ = f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf'
+ .form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries.
+ .form-group.blacklist-raw
+ = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'label-light'
+ = f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8
+ .form-text.text-muted Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com
- .form-group.row
- = f.label :after_sign_up_text, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_area :after_sign_up_text, class: 'form-control', rows: 4
- .form-text.text-muted Markdown enabled
+ .form-group
+ = f.label :after_sign_up_text, class: 'label-light'
+ = f.text_area :after_sign_up_text, class: 'form-control', rows: 4
+ .form-text.text-muted Markdown enabled
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_spam.html.haml b/app/views/admin/application_settings/_spam.html.haml
index fb38e4ae922..8f0dce962a9 100644
--- a/app/views/admin/application_settings/_spam.html.haml
+++ b/app/views/admin/application_settings/_spam.html.haml
@@ -2,64 +2,56 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :recaptcha_enabled, class: 'form-check-input'
- = f.label :recaptcha_enabled, class: 'form-check-label' do
- Enable reCAPTCHA
- %span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts
-
- .form-group.row
- = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :recaptcha_site_key, class: 'form-control'
- .form-text.text-muted
- Generate site and private keys at
- %a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha
-
- .form-group.row
- = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :recaptcha_private_key, class: 'form-control'
-
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :akismet_enabled, class: 'form-check-input'
- = f.label :akismet_enabled, class: 'form-check-label' do
- Enable Akismet
- %span.form-text.text-muted#akismet_help_block Helps prevent bots from creating issues
-
- .form-group.row
- = f.label :akismet_api_key, 'Akismet API Key', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.text_field :akismet_api_key, class: 'form-control'
- .form-text.text-muted
- Generate API key at
- %a{ href: 'http://www.akismet.com', target: 'blank' } http://www.akismet.com
-
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :unique_ips_limit_enabled, class: 'form-check-input'
- = f.label :unique_ips_limit_enabled, class: 'form-check-label' do
- Limit sign in from multiple ips
- %span.form-text.text-muted#unique_ip_help_block
- Helps prevent malicious users hide their activity
-
- .form-group.row
- = f.label :unique_ips_limit_per_user, 'IPs per user', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :unique_ips_limit_per_user, class: 'form-control'
- .form-text.text-muted
- Maximum number of unique IPs per user
-
- .form-group.row
- = f.label :unique_ips_limit_time_window, 'IP expiration time', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :unique_ips_limit_time_window, class: 'form-control'
- .form-text.text-muted
- How many seconds an IP will be counted towards the limit
+ .form-group
+ .form-check
+ = f.check_box :recaptcha_enabled, class: 'form-check-input'
+ = f.label :recaptcha_enabled, class: 'form-check-label' do
+ Enable reCAPTCHA
+ %span.form-text.text-muted#recaptcha_help_block Helps prevent bots from creating accounts
+
+ .form-group
+ = f.label :recaptcha_site_key, 'reCAPTCHA Site Key', class: 'label-light'
+ = f.text_field :recaptcha_site_key, class: 'form-control'
+ .form-text.text-muted
+ Generate site and private keys at
+ %a{ href: 'http://www.google.com/recaptcha', target: 'blank' } http://www.google.com/recaptcha
+
+ .form-group
+ = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'label-light'
+ = f.text_field :recaptcha_private_key, class: 'form-control'
+
+ .form-group
+ .form-check
+ = f.check_box :akismet_enabled, class: 'form-check-input'
+ = f.label :akismet_enabled, class: 'form-check-label' do
+ Enable Akismet
+ %span.form-text.text-muted#akismet_help_block Helps prevent bots from creating issues
+
+ .form-group
+ = f.label :akismet_api_key, 'Akismet API Key', class: 'label-light'
+ = f.text_field :akismet_api_key, class: 'form-control'
+ .form-text.text-muted
+ Generate API key at
+ %a{ href: 'http://www.akismet.com', target: 'blank' } http://www.akismet.com
+
+ .form-group
+ .form-check
+ = f.check_box :unique_ips_limit_enabled, class: 'form-check-input'
+ = f.label :unique_ips_limit_enabled, class: 'form-check-label' do
+ Limit sign in from multiple ips
+ %span.form-text.text-muted#unique_ip_help_block
+ Helps prevent malicious users hide their activity
+
+ .form-group
+ = f.label :unique_ips_limit_per_user, 'IPs per user', class: 'label-light'
+ = f.number_field :unique_ips_limit_per_user, class: 'form-control'
+ .form-text.text-muted
+ Maximum number of unique IPs per user
+
+ .form-group
+ = f.label :unique_ips_limit_time_window, 'IP expiration time', class: 'label-light'
+ = f.number_field :unique_ips_limit_time_window, class: 'form-control'
+ .form-text.text-muted
+ How many seconds an IP will be counted towards the limit
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_terminal.html.haml b/app/views/admin/application_settings/_terminal.html.haml
index ae02d07e556..543628ff0ee 100644
--- a/app/views/admin/application_settings/_terminal.html.haml
+++ b/app/views/admin/application_settings/_terminal.html.haml
@@ -2,12 +2,11 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :terminal_max_session_time, 'Max session time', class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.number_field :terminal_max_session_time, class: 'form-control'
- .form-text.text-muted
- Maximum time for web terminal websocket connection (in seconds).
- 0 for unlimited.
+ .form-group
+ = f.label :terminal_max_session_time, 'Max session time', class: 'label-light'
+ = f.number_field :terminal_max_session_time, class: 'form-control'
+ .form-text.text-muted
+ Maximum time for web terminal websocket connection (in seconds).
+ 0 for unlimited.
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_terms.html.haml b/app/views/admin/application_settings/_terms.html.haml
index 257565ce193..d3dc8659d1b 100644
--- a/app/views/admin/application_settings/_terms.html.haml
+++ b/app/views/admin/application_settings/_terms.html.haml
@@ -1,22 +1,19 @@
-= form_for @application_setting, url: admin_application_settings_path do |f|
+= form_for @application_setting, url: admin_application_settings_path, html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .col-sm-12
- .form-check
- = f.check_box :enforce_terms, class: 'form-check-input'
- = f.label :enforce_terms, class: 'form-check-label' do
- = _("Require all users to accept Terms of Service when they access GitLab.")
- .form-text.text-muted
- = _("When enabled, users cannot use GitLab until the terms have been accepted.")
- .form-group.row
- .col-sm-12
- = f.label :terms do
- = _("Terms of Service Agreement")
- .col-sm-12
- = f.text_area :terms, class: 'form-control', rows: 8
+ .form-group
+ .form-check
+ = f.check_box :enforce_terms, class: 'form-check-input'
+ = f.label :enforce_terms, class: 'form-check-label' do
+ = _("Require all users to accept Terms of Service and Privacy Policy when they access GitLab.")
.form-text.text-muted
- = _("Markdown enabled")
+ = _("When enabled, users cannot use GitLab until the terms have been accepted.")
+ .form-group
+ = f.label :terms do
+ = _("Terms of Service Agreement and Privacy Policy")
+ = f.text_area :terms, class: 'form-control', rows: 8
+ .form-text.text-muted
+ = _("Markdown enabled")
= f.submit _("Save changes"), class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_usage.html.haml b/app/views/admin/application_settings/_usage.html.haml
index c110fd4d60d..49a3ee33a85 100644
--- a/app/views/admin/application_settings/_usage.html.haml
+++ b/app/views/admin/application_settings/_usage.html.haml
@@ -2,36 +2,34 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- .offset-sm-2.col-sm-10
- .form-check
- = f.check_box :version_check_enabled, class: 'form-check-input'
- = f.label :version_check_enabled, class: 'form-check-label' do
- Enable version check
- .form-text.text-muted
- GitLab will inform you if a new version is available.
- = link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "version-check")
- about what information is shared with GitLab Inc.
- .form-group.row
- .offset-sm-2.col-sm-10
- - can_be_configured = @application_setting.usage_ping_can_be_configured?
- .form-check
- = f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
- = f.label :usage_ping_enabled, class: 'form-check-label' do
- Enable usage ping
- .form-text.text-muted
- - if can_be_configured
- To help improve GitLab and its user experience, GitLab will
- periodically collect usage information.
- = link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-ping")
- about what information is shared with GitLab Inc. Visit
- = link_to 'Cohorts', admin_cohorts_path(anchor: 'usage-ping')
- to see the JSON payload sent.
- - else
- The usage ping is disabled, and cannot be configured through this
- form. For more information, see the documentation on
- = succeed '.' do
- = link_to 'deactivating the usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'deactivate-the-usage-ping')
+ .form-group
+ .form-check
+ = f.check_box :version_check_enabled, class: 'form-check-input'
+ = f.label :version_check_enabled, class: 'form-check-label' do
+ Enable version check
+ .form-text.text-muted
+ GitLab will inform you if a new version is available.
+ = link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "version-check")
+ about what information is shared with GitLab Inc.
+ .form-group
+ - can_be_configured = @application_setting.usage_ping_can_be_configured?
+ .form-check
+ = f.check_box :usage_ping_enabled, disabled: !can_be_configured, class: 'form-check-input'
+ = f.label :usage_ping_enabled, class: 'form-check-label' do
+ Enable usage ping
+ .form-text.text-muted
+ - if can_be_configured
+ To help improve GitLab and its user experience, GitLab will
+ periodically collect usage information.
+ = link_to 'Learn more', help_page_path("user/admin_area/settings/usage_statistics", anchor: "usage-ping")
+ about what information is shared with GitLab Inc. Visit
+ = link_to 'Cohorts', admin_cohorts_path(anchor: 'usage-ping')
+ to see the JSON payload sent.
+ - else
+ The usage ping is disabled, and cannot be configured through this
+ form. For more information, see the documentation on
+ = succeed '.' do
+ = link_to 'deactivating the usage ping', help_page_path('user/admin_area/settings/usage_statistics', anchor: 'deactivate-the-usage-ping')
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_visibility_and_access.html.haml b/app/views/admin/application_settings/_visibility_and_access.html.haml
index 05520bd8d2d..4cc3e6a7d03 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -2,66 +2,57 @@
= form_errors(@application_setting)
%fieldset
- .form-group.row
- = f.label :default_branch_protection, class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
- .form-group.row.visibility-level-setting
- = f.label :default_project_visibility, class: 'col-form-label col-sm-2'
- .col-sm-10
- = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
- .form-group.row.visibility-level-setting
- = f.label :default_snippet_visibility, class: 'col-form-label col-sm-2'
- .col-sm-10
- = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
- .form-group.row.visibility-level-setting
- = f.label :default_group_visibility, class: 'col-form-label col-sm-2'
- .col-sm-10
- = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
- .form-group.row
- = f.label :restricted_visibility_levels, class: 'col-form-label col-sm-2'
- .col-sm-10
- - checkbox_name = 'application_setting[restricted_visibility_levels][]'
- = hidden_field_tag(checkbox_name)
- - restricted_level_checkboxes('restricted-visibility-help', checkbox_name, class: 'form-check-input').each do |level|
- .form-check
- = level
- %span.form-text.text-muted#restricted-visibility-help
- Selected levels cannot be used by non-admin users for groups, projects or snippets.
- If the public level is restricted, user profiles are only visible to logged in users.
- .form-group.row
- = f.label :import_sources, class: 'col-form-label col-sm-2'
- .col-sm-10
- = hidden_field_tag 'application_setting[import_sources][]'
- - import_sources_checkboxes('import-sources-help', class: 'form-check-input').each do |source|
- .form-check= source
- %span.form-text.text-muted#import-sources-help
- Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
- = link_to "(?)", help_page_path("integration/github")
- , Bitbucket
- = link_to "(?)", help_page_path("integration/bitbucket")
- and GitLab.com
- = link_to "(?)", help_page_path("integration/gitlab")
-
- .form-group.row
- .offset-sm-2.col-sm-10
+ .form-group
+ = f.label :default_branch_protection, class: 'label-light'
+ = f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
+ .form-group.visibility-level-setting
+ = f.label :default_project_visibility, class: 'label-light'
+ = render('shared/visibility_radios', model_method: :default_project_visibility, form: f, selected_level: @application_setting.default_project_visibility, form_model: Project.new)
+ .form-group.visibility-level-setting
+ = f.label :default_snippet_visibility, class: 'label-light'
+ = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new)
+ .form-group.visibility-level-setting
+ = f.label :default_group_visibility, class: 'label-light'
+ = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new)
+ .form-group
+ = f.label :restricted_visibility_levels, class: 'label-light'
+ - checkbox_name = 'application_setting[restricted_visibility_levels][]'
+ = hidden_field_tag(checkbox_name)
+ - restricted_level_checkboxes('restricted-visibility-help', checkbox_name, class: 'form-check-input').each do |level|
.form-check
- = f.check_box :project_export_enabled, class: 'form-check-input'
- = f.label :project_export_enabled, class: 'form-check-label' do
- Project export enabled
+ = level
+ %span.form-text.text-muted#restricted-visibility-help
+ Selected levels cannot be used by non-admin users for groups, projects or snippets.
+ If the public level is restricted, user profiles are only visible to logged in users.
+ .form-group
+ = f.label :import_sources, class: 'label-light'
+ = hidden_field_tag 'application_setting[import_sources][]'
+ - import_sources_checkboxes('import-sources-help', class: 'form-check-input').each do |source|
+ .form-check= source
+ %span.form-text.text-muted#import-sources-help
+ Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
+ = link_to "(?)", help_page_path("integration/github")
+ , Bitbucket
+ = link_to "(?)", help_page_path("integration/bitbucket")
+ and GitLab.com
+ = link_to "(?)", help_page_path("integration/gitlab")
+
+ .form-group
+ .form-check
+ = f.check_box :project_export_enabled, class: 'form-check-input'
+ = f.label :project_export_enabled, class: 'form-check-label' do
+ Project export enabled
- .form-group.row
- %label.col-form-label.col-sm-2 Enabled Git access protocols
- .col-sm-10
- = select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
- %span.form-text.text-muted#clone-protocol-help
- Allow only the selected protocols to be used for Git access.
+ .form-group
+ %label.label-light Enabled Git access protocols
+ = select(:application_setting, :enabled_git_access_protocol, [['Both SSH and HTTP(S)', nil], ['Only SSH', 'ssh'], ['Only HTTP(S)', 'http']], {}, class: 'form-control')
+ %span.form-text.text-muted#clone-protocol-help
+ Allow only the selected protocols to be used for Git access.
- ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
- field_name = :"#{type}_key_restriction"
- .form-group.row
- = f.label field_name, "#{type.upcase} SSH keys", class: 'col-form-label col-sm-2'
- .col-sm-10
- = f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
+ .form-group
+ = f.label field_name, "#{type.upcase} SSH keys", class: 'label-light'
+ = f.select field_name, key_restriction_options_for_select(type), {}, class: 'form-control'
= f.submit 'Save changes', class: "btn btn-success"
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index 3f440c76ee0..38607ffca1c 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -17,7 +17,7 @@
%section.settings.as-account-limit.no-animate#js-account-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
- = _('Account and limit settings')
+ = _('Account and limit')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
@@ -50,11 +50,11 @@
%section.settings.as-terms.no-animate#js-terms-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
- = _('Terms of Service')
+ = _('Terms of Service and Privacy Policy')
%button.btn.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
- = _('Include a Terms of Service agreement that all users must accept.')
+ = _('Include a Terms of Service agreement and Privacy Policy that all users must accept.')
.settings-content
= render 'terms'
@@ -169,7 +169,7 @@
.settings-content
= render 'logging'
-%section.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded) }
+%section.qa-repository-storage-settings.settings.as-repository-storage.no-animate#js-repository-storage-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Repository storage')
@@ -317,7 +317,7 @@
%section.settings.as-mirror.no-animate#js-mirror-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
- = _('Repository mirror settings')
+ = _('Repository mirror')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
diff --git a/app/views/admin/gitaly_servers/index.html.haml b/app/views/admin/gitaly_servers/index.html.haml
index d0cf5761726..9b24f411a75 100644
--- a/app/views/admin/gitaly_servers/index.html.haml
+++ b/app/views/admin/gitaly_servers/index.html.haml
@@ -6,10 +6,10 @@
- if @gitaly_servers.any?
.table-holder
%table.table.responsive-table
- %thead.d-none.d-sm-none.d-md-block
+ %thead
%tr
%th= _("Storage")
- %th= n_("Gitaly|Address")
+ %th= s_("Gitaly|Address")
%th= _("Server version")
%th= _("Git version")
%th= _("Up to date")
diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml
index dc4dccc9e0d..c8008771236 100644
--- a/app/views/admin/groups/_form.html.haml
+++ b/app/views/admin/groups/_form.html.haml
@@ -2,6 +2,9 @@
= form_errors(@group)
= render 'shared/group_form', f: f
+ = render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
+ = render_if_exists 'admin/namespace_plan', f: f
+
.form-group.row.group-description-holder
= f.label :avatar, "Group avatar", class: 'col-form-label col-sm-2'
.col-sm-10
@@ -15,6 +18,8 @@
= render 'groups/group_admin_settings', f: f
+ = render_if_exists 'namespaces/shared_runners_minutes_settings', group: @group, form: f
+
- if @group.new_record?
.form-group.row
.offset-sm-2.col-sm-10
@@ -28,3 +33,5 @@
.form-actions
= f.submit 'Save changes', class: "btn btn-save"
= link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel"
+
+= render_if_exists 'ldap_group_links/ldap_syncrhonizations', group: @group
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index e7c70a6f187..3f96988c203 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -1,3 +1,4 @@
+- group = local_assigns.fetch(:group)
- css_class = 'no-description' if group.description.blank?
%li.group-row{ class: css_class }
@@ -8,6 +9,8 @@
%span.badge.badge-pill
= storage_counter(group.storage_size)
+ = render_if_exists 'admin/namespace_plan_badge', namespace: group
+
%span
= icon('bookmark')
= number_with_delimiter(group.projects.count)
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 6d75ccd5add..a40f98ad24f 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -40,6 +40,8 @@
%strong
= @group.created_at.to_s(:medium)
+ = render_if_exists 'admin/namespace_plan_info', namespace: @group
+
%li
%span.light Storage:
%strong= storage_counter(@group.storage_size)
@@ -58,6 +60,10 @@
= group_lfs_status(@group)
= link_to icon('question-circle'), help_page_path('workflow/lfs/manage_large_binaries_with_git_lfs')
+ = render_if_exists 'namespaces/shared_runner_status', namespace: @group
+
+ = render_if_exists 'ldap_group_links/ldap_group_links_show', group: @group
+
.card
.card-header
%h3.card-title
@@ -104,7 +110,7 @@
= form_tag admin_group_members_update_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div
- = users_select_tag(:user_ids, multiple: true, email_user: true, scope: :all)
+ = users_select_tag(:user_ids, multiple: true, email_user: true, skip_ldap: @group.ldap_synced?, scope: :all)
.prepend-top-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
diff --git a/app/views/admin/services/_form.html.haml b/app/views/admin/services/_form.html.haml
index 144dceacbdd..993006e8745 100644
--- a/app/views/admin/services/_form.html.haml
+++ b/app/views/admin/services/_form.html.haml
@@ -7,5 +7,4 @@
= render 'shared/service_settings', form: form, subject: @service
.footer-block.row-content-block
- .form-actions
- = form.submit 'Save', class: 'btn btn-save'
+ = form.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml
index 4b3c52af16a..8ca9fb4512e 100644
--- a/app/views/award_emoji/_awards_block.html.haml
+++ b/app/views/award_emoji/_awards_block.html.haml
@@ -12,9 +12,9 @@
- if can?(current_user, :award_emoji, awardable)
.award-menu-holder.js-award-holder
%button.btn.award-control.has-tooltip.js-add-award{ type: 'button',
- 'aria-label': 'Add reaction',
+ 'aria-label': _('Add reaction'),
class: ("js-user-authored" if user_authored),
- data: { title: 'Add reaction', placement: "bottom" } }
+ data: { title: _('Add reaction'), placement: "bottom" } }
%span{ class: "award-control-icon award-control-icon-neutral" }= custom_icon('emoji_slightly_smiling_face')
%span{ class: "award-control-icon award-control-icon-positive" }= custom_icon('emoji_smiley')
%span{ class: "award-control-icon award-control-icon-super-positive" }= custom_icon('emoji_smile')
diff --git a/app/views/ci/variables/_variable_row.html.haml b/app/views/ci/variables/_variable_row.html.haml
index 571eb28f195..6ee55836dd2 100644
--- a/app/views/ci/variables/_variable_row.html.haml
+++ b/app/views/ci/variables/_variable_row.html.haml
@@ -43,5 +43,6 @@
%span.toggle-icon
= sprite_icon('status_success_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-checked')
= sprite_icon('status_failed_borderless', size: 16, css_class: 'toggle-icon-svg toggle-status-unchecked')
+ = render_if_exists 'ci/variables/environment_scope', form_field: form_field, variable: variable
%button.js-row-remove-button.ci-variable-row-remove-button{ type: 'button', 'aria-label': s_('CiVariables|Remove variable row') }
= icon('minus-circle')
diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml
index 2554b2688bb..ee7369f54a9 100644
--- a/app/views/devise/shared/_signup_box.html.haml
+++ b/app/views/devise/shared/_signup_box.html.haml
@@ -22,6 +22,13 @@
= f.label :password
= f.password_field :password, class: "form-control bottom", required: true, pattern: ".{#{@minimum_password_length},}", title: "Minimum length is #{@minimum_password_length} characters."
%p.gl-field-hint Minimum length is #{@minimum_password_length} characters
+ - if Gitlab::CurrentSettings.current_application_settings.enforce_terms?
+ .form-group
+ = check_box_tag :terms_opt_in, '1', false, required: true
+ = label_tag :terms_opt_in do
+ - terms_link = link_to s_("I accept the|Terms of Service and Privacy Policy"), terms_path, target: "_blank"
+ - accept_terms_label = _("I accept the %{terms_link}") % { terms_link: terms_link }
+ = accept_terms_label.html_safe
%div
- if Gitlab::Recaptcha.enabled?
= recaptcha_tags
diff --git a/app/views/errors/access_denied.html.haml b/app/views/errors/access_denied.html.haml
index 227c7884915..8ae29b9d337 100644
--- a/app/views/errors/access_denied.html.haml
+++ b/app/views/errors/access_denied.html.haml
@@ -1,4 +1,4 @@
-- message = local_assigns.fetch(:message)
+- message = local_assigns.fetch(:message, nil)
- content_for(:title, 'Access Denied')
= image_tag('illustrations/error-403.svg', alt: '403', lazy: false)
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 6a0321bcd2b..13d584f5f1d 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -25,9 +25,10 @@
%span.badge= @members.total_count
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form flex-project-members-form' do
.form-group
- = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
- %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
- = icon("search")
+ .position-relative.append-right-8
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
+ %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
+ = icon("search")
- if can_manage_members
= render 'shared/members/filter_2fa_dropdown'
= render 'shared/members/sort_dropdown'
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index ac7e12fcd0b..db7eaff6658 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,21 +1,32 @@
-- page_title 'Labels'
-
+- @no_container = true
+- page_title "Labels"
+- can_admin_label = can?(current_user, :admin_label, @group)
+- hide_class = ''
+- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
- issuables = ['issues', 'merge requests']
-.top-area.adjust
- .nav-text
- = _("Labels can be applied to %{features}. Group labels are available for any project within the group.") % { features: issuables.to_sentence }
+- if can_admin_label
+ - content_for(:header_content) do
+ .nav-controls
+ = link_to _('New label'), new_group_label_path(@group), class: "btn btn-new"
+
+- if @labels.exists?
+ #promote-label-modal
+ %div{ class: container_class }
+ .top-area.adjust
+ .nav-text
+ = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
- .nav-controls
- - if can?(current_user, :admin_label, @group)
- = link_to "New label", new_group_label_path(@group), class: "btn btn-new"
+ .labels-container.prepend-top-5
+ .other-labels
+ - if can_admin_label
+ %h5{ class: ('hide' if hide) } Labels
+ %ul.content-list.manage-labels-list.js-other-labels
+ = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false }
+ = paginate @labels, theme: 'gitlab'
+- else
+ = render 'shared/empty_states/labels'
-.labels
- .other-labels
- - if @labels.present?
- %ul.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', subject: @group, collection: @labels, as: :label
- = paginate @labels, theme: 'gitlab'
- - else
- .nothing-here-block
- = _("No labels created yet.")
+%template#js-badge-item-template
+ %li.label-link-item.js-priority-badge.inline.prepend-left-10
+ .label-badge.label-badge-blue= _('Prioritized label')
diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml
index ff2b418e479..647948c7dff 100644
--- a/app/views/groups/settings/ci_cd/show.html.haml
+++ b/app/views/groups/settings/ci_cd/show.html.haml
@@ -18,7 +18,7 @@
%section.settings#runners-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
- = _('Runners settings')
+ = _('Runners')
%button.btn.btn-default.js-settings-toggle{ type: "button" }
= expanded ? _('Collapse') : _('Expand')
%p
diff --git a/app/views/import/gitlab_projects/new.html.haml b/app/views/import/gitlab_projects/new.html.haml
index 2d059e78490..f311ac98ac6 100644
--- a/app/views/import/gitlab_projects/new.html.haml
+++ b/app/views/import/gitlab_projects/new.html.haml
@@ -37,6 +37,6 @@
.form-group
= file_field_tag :file, class: ''
.row
- .form-actions
+ .form-actions.col-sm-12
= submit_tag 'Import project', class: 'btn btn-create'
= link_to 'Cancel', new_project_path, class: 'btn btn-cancel'
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 02bdfe9aa3c..9253a0652da 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -25,7 +25,7 @@
%title= page_title(site_name)
%meta{ name: "description", content: page_description }
- = favicon_link_tag favicon, id: 'favicon'
+ = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
= stylesheet_link_tag "application", media: "all"
= stylesheet_link_tag "print", media: "print"
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 82ca7252424..81f35615555 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html.devise-layout-html{ class: system_message_class }
= render "layouts/head"
- %body.ui_indigo.login-page.application.navless{ data: { page: body_data_page } }
+ %body.ui-indigo.login-page.application.navless{ data: { page: body_data_page } }
.page-wrap
= render "layouts/header/empty"
.login-page-broadcast
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index adf90cb7667..52805e0da73 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html{ lang: "en", class: system_message_class }
= render "layouts/head"
- %body.ui_indigo.login-page.application.navless
+ %body.ui-indigo.login-page.application.navless
= render "layouts/header/empty"
= render "layouts/broadcast"
.container.navless-container
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 1bca837a311..5cec443e969 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -61,7 +61,7 @@
- if header_link?(:sign_in)
%li.nav-item
%div
- = link_to "Sign in / Register", new_session_path(:user, redirect_to_referer: 'yes'), class: 'nav-link btn btn-sign-in'
+ = link_to "Sign in / Register", new_session_path(:user, redirect_to_referer: 'yes'), class: 'btn btn-sign-in'
%button.navbar-toggler.d-block.d-sm-none{ type: 'button' }
%span.sr-only Toggle navigation
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index e63e7772ba3..8f1078bd41d 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -9,13 +9,7 @@
.col-lg-8.application-theme
- Gitlab::Themes.each do |theme|
= label_tag do
- .preview{ class: theme.name.downcase }
- .preview-row
- .quadrant.one
- .quadrant.two
- .preview-row
- .quadrant.three
- .quadrant.four
+ .preview{ class: theme.css_class }
= f.radio_button :theme_id, theme.id, checked: Gitlab::Themes.for_user(@user).id == theme.id
= theme.name
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index 7ff7466e561..87b165e581a 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -9,10 +9,10 @@
.tree-holder
.nav-block
%ul.breadcrumb.repo-breadcrumb
- %li
+ %li.breadcrumb-item
= link_to 'Artifacts', browse_project_job_artifacts_path(@project, @build)
- path_breadcrumbs do |title, path|
- %li
+ %li.breadcrumb-item
= link_to truncate(title, length: 40), browse_project_job_artifacts_path(@project, @build, path)
.tree-controls
diff --git a/app/views/projects/clusters/gcp/_form.html.haml b/app/views/projects/clusters/gcp/_form.html.haml
index ca7a6d5a886..59c4eeec17a 100644
--- a/app/views/projects/clusters/gcp/_form.html.haml
+++ b/app/views/projects/clusters/gcp/_form.html.haml
@@ -15,7 +15,7 @@
= field.fields_for :provider_gcp, @cluster.provider_gcp do |provider_gcp_field|
.form-group
- = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project ID')
+ = provider_gcp_field.label :gcp_project_id, s_('ClusterIntegration|Google Cloud Platform project')
.js-gcp-project-id-dropdown-entry-point{ data: { docsUrl: 'https://console.cloud.google.com/home/dashboard' } }
= provider_gcp_field.hidden_field :gcp_project_id
.dropdown
diff --git a/app/views/projects/clusters/user/_form.html.haml b/app/views/projects/clusters/user/_form.html.haml
index 2e92524ce8f..db57da99ec7 100644
--- a/app/views/projects/clusters/user/_form.html.haml
+++ b/app/views/projects/clusters/user/_form.html.haml
@@ -1,27 +1,27 @@
= form_for @cluster, url: user_namespace_project_clusters_path(@project.namespace, @project), as: :cluster do |field|
= form_errors(@cluster)
.form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name')
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
.form-group
- = field.label :environment_scope, s_('ClusterIntegration|Environment scope')
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-light'
= field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?(@project), placeholder: s_('ClusterIntegration|Environment scope')
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light'
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
.form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light'
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
.form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light'
= platform_kubernetes_field.text_field :token, class: 'form-control', placeholder: s_('ClusterIntegration|Service token'), autocomplete: 'off'
.form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
.form-group
diff --git a/app/views/projects/clusters/user/_show.html.haml b/app/views/projects/clusters/user/_show.html.haml
index 77d7a055474..4d117f435dc 100644
--- a/app/views/projects/clusters/user/_show.html.haml
+++ b/app/views/projects/clusters/user/_show.html.haml
@@ -1,20 +1,20 @@
= form_for @cluster, url: namespace_project_cluster_path(@project.namespace, @project, @cluster), as: :cluster do |field|
= form_errors(@cluster)
.form-group
- = field.label :name, s_('ClusterIntegration|Kubernetes cluster name')
+ = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-light'
= field.text_field :name, class: 'form-control', placeholder: s_('ClusterIntegration|Kubernetes cluster name')
= field.fields_for :platform_kubernetes, @cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
- = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL')
+ = platform_kubernetes_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-light'
= platform_kubernetes_field.text_field :api_url, class: 'form-control', placeholder: s_('ClusterIntegration|API URL')
.form-group
- = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate')
+ = platform_kubernetes_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-light'
= platform_kubernetes_field.text_area :ca_cert, class: 'form-control', placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)')
.form-group
- = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token')
+ = platform_kubernetes_field.label :token, s_('ClusterIntegration|Token'), class: 'label-light'
.input-group
= platform_kubernetes_field.text_field :token, class: 'form-control js-cluster-token', type: 'password', placeholder: s_('ClusterIntegration|Token'), autocomplete: 'off'
%span.input-group-append.clipboard-addon
@@ -23,7 +23,7 @@
= s_('ClusterIntegration|Show')
.form-group
- = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)')
+ = platform_kubernetes_field.label :namespace, s_('ClusterIntegration|Project namespace (optional, unique)'), class: 'label-light'
= platform_kubernetes_field.text_field :namespace, class: 'form-control', placeholder: s_('ClusterIntegration|Project namespace')
.form-group
diff --git a/app/views/projects/commit/_change.html.haml b/app/views/projects/commit/_change.html.haml
index 30605927fd1..3d97e93c9e9 100644
--- a/app/views/projects/commit/_change.html.haml
+++ b/app/views/projects/commit/_change.html.haml
@@ -20,18 +20,18 @@
%span{ "aria-hidden": true } &times;
.modal-body
- if description
- %p.append-bottom-20= description
+ %p= description
= form_tag [type.underscore, @project.namespace.becomes(Namespace), @project, commit], method: :post, remote: false, class: "js-#{type}-form js-requires-input" do
- .form-group.row.branch
- = label_tag 'start_branch', branch_label, class: 'col-form-label col-sm-2'
- .col-sm-10
- = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
- = dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: project_branches_path(@project), submit_form_on_click: false } })
+ .form-group.branch
+ = label_tag 'start_branch', branch_label, class: 'label-light'
- - if can?(current_user, :push_code, @project)
- = render 'shared/new_merge_request_checkbox'
- - else
- = hidden_field_tag 'create_merge_request', 1, id: nil
+ = hidden_field_tag :start_branch, @project.default_branch, id: 'start_branch'
+ = dropdown_tag(@project.default_branch, options: { title: s_("BranchSwitcherTitle|Switch branch"), filter: true, placeholder: s_("BranchSwitcherPlaceholder|Search branches"), toggle_class: 'js-project-refs-dropdown dynamic', dropdown_class: 'dropdown-menu-selectable', data: { field_name: "start_branch", selected: @project.default_branch, start_branch: @project.default_branch, refs_url: project_branches_path(@project), submit_form_on_click: false } })
+
+ - if can?(current_user, :push_code, @project)
+ = render 'shared/new_merge_request_checkbox'
+ - else
+ = hidden_field_tag 'create_merge_request', 1, id: nil
.form-actions
= submit_tag label, class: 'btn btn-create'
= link_to _("Cancel"), '#', class: "btn btn-cancel", "data-dismiss" => "modal"
diff --git a/app/views/projects/commit/branches.html.haml b/app/views/projects/commit/branches.html.haml
index a91e31afc2b..0b8e5105bc0 100644
--- a/app/views/projects/commit/branches.html.haml
+++ b/app/views/projects/commit/branches.html.haml
@@ -6,7 +6,7 @@
- if @branches.any? || @tags.any? || @tags_limit_exceeded
%span
- = link_to "#", class: "js-details-expand label label-gray ref-name" do
+ = link_to "#", class: "js-details-expand badge badge-gray ref-name" do
= sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle')
%span.js-details-content.hide
= commit_branches_links(@project, @branches)
diff --git a/app/views/projects/deploy_keys/_index.html.haml b/app/views/projects/deploy_keys/_index.html.haml
index 6af57d3ab26..fb1ea471dec 100644
--- a/app/views/projects/deploy_keys/_index.html.haml
+++ b/app/views/projects/deploy_keys/_index.html.haml
@@ -1,5 +1,5 @@
- expanded = Rails.env.test?
-%section.settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-deploy-keys-settings.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
Deploy Keys
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 77ba422e87e..9f175d2376f 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -7,7 +7,7 @@
%section.settings.general-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
- General project settings
+ General project
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
@@ -82,10 +82,10 @@
= render_if_exists 'projects/issues_settings'
- %section.settings.merge-requests-feature.no-animate{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] }
+ %section.qa-merge-request-settings.settings.merge-requests-feature.no-animate{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] }
.settings-header
%h4
- Merge request settings
+ Merge request
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
@@ -101,10 +101,10 @@
= render 'export', project: @project
- %section.settings.advanced-settings.no-animate{ class: ('expanded' if expanded) }
+ %section.qa-advanced-settings.settings.advanced-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
- Advanced settings
+ Advanced
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index bf6fc8af12d..69753427d17 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -44,14 +44,14 @@
.git-empty
%fieldset
%h5 Git global setup
- %pre.card.bg-light
+ %pre.bg-light
:preserve
git config --global user.name "#{h git_user_name}"
git config --global user.email "#{h git_user_email}"
%fieldset
%h5 Create a new repository
- %pre.card.bg-light
+ %pre.bg-light
:preserve
git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
cd #{h @project.path}
@@ -64,7 +64,7 @@
%fieldset
%h5 Existing folder
- %pre.card.bg-light
+ %pre.bg-light
:preserve
cd existing_folder
git init
@@ -77,7 +77,7 @@
%fieldset
%h5 Existing Git repository
- %pre.card.bg-light
+ %pre.bg-light
:preserve
cd existing_repo
git remote rename origin old-origin
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 9c78bade254..fb5b0fc15c9 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -1,40 +1,44 @@
- @no_container = true
- page_title "Labels"
-- hide_class = ''
- can_admin_label = can?(current_user, :admin_label, @project)
+- hide_class = ''
+
+- if can_admin_label
+ - content_for(:header_content) do
+ .nav-controls
+ = link_to _('New label'), new_project_label_path(@project), class: "btn btn-new"
- if @labels.exists? || @prioritized_labels.exists?
#promote-label-modal
%div{ class: container_class }
.top-area.adjust
.nav-text
- Labels can be applied to issues and merge requests.
+ = _('Labels can be applied to issues and merge requests.')
- if can_admin_label
- Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.
+ = _('Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging.')
- - if can_admin_label
- .nav-controls
- = link_to new_project_label_path(@project), class: "btn btn-new" do
- New label
-
- .labels
+ .labels-container.prepend-top-5
- if can_admin_label
-# Only show it in the first page
- hide = @available_labels.empty? || (params[:page].present? && params[:page] != '1')
.prioritized-labels{ class: ('hide' if hide) }
- %h5 Prioritized Labels
- %ul.content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) }
- #js-priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" }
+ %h5.prepend-top-10= _('Prioritized Labels')
+ .content-list.manage-labels-list.js-prioritized-labels{ "data-url" => set_priorities_project_labels_path(@project) }
+ #js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty?}" }
= render 'shared/empty_states/priority_labels'
- if @prioritized_labels.present?
- = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label
+ = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label, locals: { force_priority: true }
- if @labels.present?
.other-labels
- if can_admin_label
- %h5{ class: ('hide' if hide) } Other Labels
- %ul.content-list.manage-labels-list.js-other-labels
+ %h5{ class: ('hide' if hide) }= _('Other Labels')
+ .content-list.manage-labels-list.js-other-labels
= render partial: 'shared/label', subject: @project, collection: @labels, as: :label
= paginate @labels, theme: 'gitlab'
- else
= render 'shared/empty_states/labels'
+
+%template#js-badge-item-template
+ %li.label-link-item.js-priority-badge.inline.prepend-left-10
+ .label-badge.label-badge-blue= _('Prioritized label')
diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml
index 01e38ffee20..2f1877a15c2 100644
--- a/app/views/projects/merge_requests/show.html.haml
+++ b/app/views/projects/merge_requests/show.html.haml
@@ -32,26 +32,25 @@
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
- .nav-links.scrolling-tabs.nav.nav-tabs
- %ul.merge-request-tabs.nav-tabs.nav
- %li.notes-tab
- = tab_link_for @merge_request, :show, force_link: @commit.present? do
- Discussion
- %span.badge.badge-pill= @merge_request.related_notes.user.count
- - if @merge_request.source_project
- %li.commits-tab
- = tab_link_for @merge_request, :commits do
- Commits
- %span.badge.badge-pill= @commits_count
- - if @pipelines.any?
- %li.pipelines-tab
- = tab_link_for @merge_request, :pipelines do
- Pipelines
- %span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
- %li.diffs-tab
- = tab_link_for @merge_request, :diffs do
- Changes
- %span.badge.badge-pill= @merge_request.diff_size
+ %ul.merge-request-tabs.nav-tabs.nav.nav-links.scrolling-tabs
+ %li.notes-tab
+ = tab_link_for @merge_request, :show, force_link: @commit.present? do
+ Discussion
+ %span.badge.badge-pill= @merge_request.related_notes.user.count
+ - if @merge_request.source_project
+ %li.commits-tab
+ = tab_link_for @merge_request, :commits do
+ Commits
+ %span.badge.badge-pill= @commits_count
+ - if @pipelines.any?
+ %li.pipelines-tab
+ = tab_link_for @merge_request, :pipelines do
+ Pipelines
+ %span.badge.badge-pill.js-pipelines-mr-count= @pipelines.size
+ %li.diffs-tab
+ = tab_link_for @merge_request, :diffs do
+ Changes
+ %span.badge.badge-pill= @merge_request.diff_size
- if has_vue_discussions_cookie?
#js-vue-discussion-counter
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 118391aac64..951f80b378d 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -1,7 +1,7 @@
.tabs-holder
%ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator.nav.nav-tabs
%li.js-pipeline-tab-link
- = link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
+ = 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: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
@@ -43,12 +43,36 @@
= render partial: "projects/stage/stage", collection: pipeline.legacy_stages, as: :stage
- if @pipeline.failed_builds.present?
- #js-tab-failures.build-failures.tab-pane
- - @pipeline.failed_builds.each_with_index do |build, index|
- .build-state
- %span.ci-status-icon-failed= custom_icon('icon_status_failed')
- %span.stage
- = build.stage.titleize
- %span.build-name
- = link_to build.name, pipeline_job_url(pipeline, build)
- %pre.build-log= build_summary(build, skip: index >= 10)
+ #js-tab-failures.build-failures.tab-pane.build-page
+ %table.table.responsive-table.ci-table.responsive-table-sm-rounded
+ %thead
+ %th.table-th-transparent
+ %th.table-th-transparent= _("Name")
+ %th.table-th-transparent= _("Stage")
+ %th.table-th-transparent= _("Failure")
+
+ %tbody
+ - @pipeline.failed_builds.each_with_index do |build, index|
+ - job = build.present(current_user: current_user)
+ %tr.build-state.responsive-table-border-start
+ %td.responsive-table-cell.ci-status-icon-failed{ data: { column: "Status"} }
+ .d-none.d-md-block.build-icon
+ = custom_icon("icon_status_#{build.status}")
+ .d-md-none.build-badge
+ = render "ci/status/badge", link: false, status: job.detailed_status(current_user)
+ %td.responsive-table-cell.build-name{ data: { column: _("Name")} }
+ = link_to build.name, pipeline_job_url(pipeline, build)
+ %td.responsive-table-cell.build-stage{ data: { column: _("Stage")} }
+ = build.stage.titleize
+ %td.responsive-table-cell.build-failure{ data: { column: _("Failure")} }
+ = build.present.callout_failure_message
+ %td.responsive-table-cell.build-actions
+ = link_to retry_project_job_path(build.project, build, return_to: request.original_url), method: :post, title: _('Retry'), class: 'btn btn-build' do
+ = icon('repeat')
+ %tr.build-trace-row.responsive-table-border-end
+ %td
+ %td.responsive-table-cell.build-trace-container{ colspan: 4 }
+ %pre.build-trace.build-trace-rounded
+ %code.bash.js-build-output
+ = build_summary(build)
+
diff --git a/app/views/projects/project_members/_groups.html.haml b/app/views/projects/project_members/_groups.html.haml
index 128f52ff648..3f05e06b0c6 100644
--- a/app/views/projects/project_members/_groups.html.haml
+++ b/app/views/projects/project_members/_groups.html.haml
@@ -3,5 +3,5 @@
Groups with access to
%strong= @project.name
%span.badge.badge-pill= group_links.size
- %ul.content-list
+ %ul.content-list.members-list
= render partial: 'shared/members/group', collection: group_links, as: :group_link
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index a30870a241c..0c5a187f208 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -9,9 +9,10 @@
%span.badge.badge-pill= members.total_count
= form_tag project_project_members_path(project), method: :get, class: 'form-inline member-search-form flex-project-members-form' do
.form-group
- = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
- %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
- = icon("search")
+ .position-relative
+ = search_field_tag :search, params[:search], { placeholder: 'Find existing members by name', class: 'form-control', spellcheck: false }
+ %button.member-search-btn{ type: "submit", "aria-label" => "Submit search" }
+ = icon("search")
= render 'shared/members/sort_dropdown'
%ul.content-list.members-list
= render partial: 'shared/members/member', collection: members, as: :member
diff --git a/app/views/projects/protected_branches/shared/_branches_list.html.haml b/app/views/projects/protected_branches/shared/_branches_list.html.haml
index a2cd7752fc4..9a06eca89bb 100644
--- a/app/views/projects/protected_branches/shared/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/shared/_branches_list.html.haml
@@ -1,7 +1,7 @@
.protected-branches-list.js-protected-branches-list.qa-protected-branches-list
- if @protected_branches.empty?
- .card-header
- %h3.card-title
+ .card-header.bg-white
+ %h3.card-title.mb-0
Protected branch (#{@protected_branches_count})
%p.settings-message.text-center
There are currently no protected branches, protect a branch with the form above.
diff --git a/app/views/projects/protected_branches/shared/_index.html.haml b/app/views/projects/protected_branches/shared/_index.html.haml
index 846f8858d14..4f1c6c92484 100644
--- a/app/views/projects/protected_branches/shared/_index.html.haml
+++ b/app/views/projects/protected_branches/shared/_index.html.haml
@@ -1,6 +1,6 @@
- expanded = Rails.env.test?
-%section.settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-protected-branches-settings.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
Protected Branches
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index b4787032966..d6f758608a0 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -13,7 +13,7 @@
= form_for(@release, method: :put, url: project_tag_release_path(@project, @tag.name), html: { class: 'common-note-form release-form js-quick-submit' }) do |f|
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
- = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..."
+ = render 'projects/zen', f: f, attr: :description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here…"
= render 'shared/notes/hints'
.error-alert
.prepend-top-default
diff --git a/app/views/projects/services/mattermost_slash_commands/_help.html.haml b/app/views/projects/services/mattermost_slash_commands/_help.html.haml
index 62bef77be97..b20614dc88f 100644
--- a/app/views/projects/services/mattermost_slash_commands/_help.html.haml
+++ b/app/views/projects/services/mattermost_slash_commands/_help.html.haml
@@ -1,18 +1,19 @@
- enabled = Gitlab.config.mattermost.enabled
-.card
- %p
- This service allows users to perform common operations on this
- project by entering slash commands in Mattermost.
- = link_to help_page_path('user/project/integrations/mattermost_slash_commands.md'), target: '_blank' do
- View documentation
- = icon('external-link')
- %p.inline
- See list of available commands in Mattermost after setting up this service,
- by entering
- %kbd.inline /&lt;trigger&gt; help
- - unless enabled || @service.template?
- = render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
+.info-well
+ .well-segment
+ %p
+ This service allows users to perform common operations on this
+ project by entering slash commands in Mattermost.
+ = link_to help_page_path('user/project/integrations/mattermost_slash_commands.md'), target: '_blank' do
+ View documentation
+ = icon('external-link')
+ %p.inline
+ See list of available commands in Mattermost after setting up this service,
+ by entering
+ %kbd.inline /&lt;trigger&gt; help
+ - unless enabled || @service.template?
+ = render 'projects/services/mattermost_slash_commands/detailed_help', subject: @service
- if enabled && !@service.template?
= render 'projects/services/mattermost_slash_commands/installation_info', subject: @service
diff --git a/app/views/projects/services/prometheus/_configuration_banner.html.haml b/app/views/projects/services/prometheus/_configuration_banner.html.haml
index 2cc2a6b2b5b..898b55e4b39 100644
--- a/app/views/projects/services/prometheus/_configuration_banner.html.haml
+++ b/app/views/projects/services/prometheus/_configuration_banner.html.haml
@@ -2,7 +2,7 @@
= s_('PrometheusService|Auto configuration')
- if service.manual_configuration?
- .well
+ .info-well
= s_('PrometheusService|To enable the installation of Prometheus on your clusters, deactivate the manual configuration below')
- else
.container-fluid
diff --git a/app/views/projects/services/prometheus/_help.html.haml b/app/views/projects/services/prometheus/_help.html.haml
index 15e7362c2ba..35d655e4b32 100644
--- a/app/views/projects/services/prometheus/_help.html.haml
+++ b/app/views/projects/services/prometheus/_help.html.haml
@@ -5,5 +5,5 @@
= s_('PrometheusService|Manual configuration')
- unless @service.editable?
- .card
+ .info-well
= s_('PrometheusService|To enable manual configuration, uninstall Prometheus from your clusters')
diff --git a/app/views/projects/services/slack_slash_commands/_help.html.haml b/app/views/projects/services/slack_slash_commands/_help.html.haml
index 44f58ad05e5..9d045d84b52 100644
--- a/app/views/projects/services/slack_slash_commands/_help.html.haml
+++ b/app/views/projects/services/slack_slash_commands/_help.html.haml
@@ -1,99 +1,100 @@
- pretty_name = defined?(@project) ? @project.full_name : 'namespace / path'
- run_actions_text = "Perform common operations on GitLab project: #{pretty_name}"
-.card
- %p
- This service allows users to perform common operations on this
- project by entering slash commands in Slack.
- = link_to help_page_path('user/project/integrations/slack_slash_commands.md'), target: '_blank' do
- View documentation
- = icon('external-link')
- %p.inline
- See list of available commands in Slack after setting up this service,
- by entering
- %kbd.inline /&lt;command&gt; help
- - unless @service.template?
- %p To setup this service:
- %ul.list-unstyled.indent-list
- %li
- 1.
- = link_to 'https://my.slack.com/services/new/slash-commands', target: '_blank', rel: 'noreferrer noopener nofollow' do
- Add a slash command
- = icon('external-link')
- in your Slack team with these options:
+.info-well
+ .well-segment
+ %p
+ This service allows users to perform common operations on this
+ project by entering slash commands in Slack.
+ = link_to help_page_path('user/project/integrations/slack_slash_commands.md'), target: '_blank' do
+ View documentation
+ = icon('external-link')
+ %p.inline
+ See list of available commands in Slack after setting up this service,
+ by entering
+ %kbd.inline /&lt;command&gt; help
+ - unless @service.template?
+ %p To setup this service:
+ %ul.list-unstyled.indent-list
+ %li
+ 1.
+ = link_to 'https://my.slack.com/services/new/slash-commands', target: '_blank', rel: 'noreferrer noopener nofollow' do
+ Add a slash command
+ = icon('external-link')
+ in your Slack team with these options:
- %hr
+ %hr
- .help-form
- .form-group
- = label_tag nil, 'Command', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.text-block
- %p Fill in the word that works best for your team.
- %p
- Suggestions:
- %code= 'gitlab'
- %code= @project.path # Path contains no spaces, but dashes
- %code= @project.full_path
+ .help-form
+ .form-group
+ = label_tag nil, 'Command', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.text-block
+ %p Fill in the word that works best for your team.
+ %p
+ Suggestions:
+ %code= 'gitlab'
+ %code= @project.path # Path contains no spaces, but dashes
+ %code= @project.full_path
- .form-group
- = label_tag :url, 'URL', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.input-group
- = text_field_tag :url, service_trigger_url(subject), class: 'form-control form-control-sm', readonly: 'readonly'
- .input-group-append
- = clipboard_button(target: '#url', class: 'input-group-text')
+ .form-group
+ = label_tag :url, 'URL', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.input-group
+ = text_field_tag :url, service_trigger_url(subject), class: 'form-control form-control-sm', readonly: 'readonly'
+ .input-group-append
+ = clipboard_button(target: '#url', class: 'input-group-text')
- .form-group
- = label_tag nil, 'Method', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.text-block POST
+ .form-group
+ = label_tag nil, 'Method', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.text-block POST
- .form-group
- = label_tag :customize_name, 'Customize name', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.input-group
- = text_field_tag :customize_name, 'GitLab', class: 'form-control form-control-sm', readonly: 'readonly'
- .input-group-append
- = clipboard_button(target: '#customize_name', class: 'input-group-text')
+ .form-group
+ = label_tag :customize_name, 'Customize name', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.input-group
+ = text_field_tag :customize_name, 'GitLab', class: 'form-control form-control-sm', readonly: 'readonly'
+ .input-group-append
+ = clipboard_button(target: '#customize_name', class: 'input-group-text')
- .form-group
- = label_tag nil, 'Customize icon', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.text-block
- = image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36)
- = link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank', rel: 'noopener noreferrer')
+ .form-group
+ = label_tag nil, 'Customize icon', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.text-block
+ = image_tag(asset_url('slash-command-logo.png'), width: 36, height: 36)
+ = link_to('Download image', asset_url('gitlab_logo.png'), class: 'btn btn-sm', target: '_blank', rel: 'noopener noreferrer')
- .form-group
- = label_tag nil, 'Autocomplete', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.text-block Show this command in the autocomplete list
+ .form-group
+ = label_tag nil, 'Autocomplete', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.text-block Show this command in the autocomplete list
- .form-group
- = label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.input-group
- = text_field_tag :autocomplete_description, run_actions_text, class: 'form-control form-control-sm', readonly: 'readonly'
- .input-group-append
- = clipboard_button(target: '#autocomplete_description', class: 'input-group-text')
+ .form-group
+ = label_tag :autocomplete_description, 'Autocomplete description', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.input-group
+ = text_field_tag :autocomplete_description, run_actions_text, class: 'form-control form-control-sm', readonly: 'readonly'
+ .input-group-append
+ = clipboard_button(target: '#autocomplete_description', class: 'input-group-text')
- .form-group
- = label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.input-group
- = text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control form-control-sm', readonly: 'readonly'
- .input-group-append
- = clipboard_button(target: '#autocomplete_usage_hint', class: 'input-group-text')
+ .form-group
+ = label_tag :autocomplete_usage_hint, 'Autocomplete usage hint', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.input-group
+ = text_field_tag :autocomplete_usage_hint, '[help]', class: 'form-control form-control-sm', readonly: 'readonly'
+ .input-group-append
+ = clipboard_button(target: '#autocomplete_usage_hint', class: 'input-group-text')
- .form-group
- = label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-12 col-form-label'
- .col-sm-10.col-12.input-group
- = text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control form-control-sm', readonly: 'readonly'
- .input-group-append
- = clipboard_button(target: '#descriptive_label', class: 'input-group-text')
+ .form-group
+ = label_tag :descriptive_label, 'Descriptive label', class: 'col-sm-2 col-12 col-form-label'
+ .col-sm-10.col-12.input-group
+ = text_field_tag :descriptive_label, 'Perform common operations on GitLab project', class: 'form-control form-control-sm', readonly: 'readonly'
+ .input-group-append
+ = clipboard_button(target: '#descriptive_label', class: 'input-group-text')
- %hr
+ %hr
- %ul.list-unstyled.indent-list
- %li
- 2. Paste the
- %strong Token
- into the field below
- %li
- 3. Select the
- %strong Active
- checkbox, press
- %strong Save changes
- and start using GitLab inside Slack!
+ %ul.list-unstyled.indent-list
+ %li
+ 2. Paste the
+ %strong Token
+ into the field below
+ %li
+ 3. Select the
+ %strong Active
+ checkbox, press
+ %strong Save changes
+ and start using GitLab inside Slack!
diff --git a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
index bbabb98dafe..4359362bb05 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -1,43 +1,66 @@
-.row.prepend-top-default
+.row
.col-lg-12
= form_for @project, url: project_settings_ci_cd_path(@project) do |f|
= form_errors(@project)
- %fieldset.builds-feature
+ %fieldset.builds-feature.js-auto-devops-settings
.form-group
- message = auto_devops_warning_message(@project)
- ci_file_formatted = '<code>.gitlab-ci.yml</code>'.html_safe
- if message
- %p.settings-message.text-center
+ %p.auto-devops-warning-message.settings-message.text-center
= message.html_safe
= f.fields_for :auto_devops_attributes, @auto_devops do |form|
- .form-check
- = form.radio_button :enabled, 'true', class: 'form-check-input'
- = form.label :enabled_true, class: 'form-check-label' do
- %strong= s_('CICD|Enable Auto DevOps')
- %br
- = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted }
+ .card.auto-devops-card
+ .card-body
+ .form-check
+ = form.radio_button :enabled, 'true', class: 'form-check-input js-toggle-extra-settings'
+ = form.label :enabled_true, class: 'form-check-label' do
+ %strong= s_('CICD|Enable Auto DevOps')
+ .form-text.text-muted
+ = s_('CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project.').html_safe % { ci_file: ci_file_formatted }
- .form-check
- = form.radio_button :enabled, 'false', class: 'form-check-input'
- = form.label :enabled_false, class: 'form-check-label' do
- %strong= s_('CICD|Disable Auto DevOps')
- %br
- = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted }
+ .card.auto-devops-card
+ .card-body
+ .form-check
+ = form.radio_button :enabled, '', class: 'form-check-input js-toggle-extra-settings'
+ = form.label :enabled_, class: 'form-check-label' do
+ %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" }
+ .form-text.text-muted
+ = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted }
- .form-check
- = form.radio_button :enabled, '', class: 'form-check-input'
- = form.label :enabled_, class: 'form-check-label' do
- %strong= s_('CICD|Instance default (%{state})') % { state: "#{Gitlab::CurrentSettings.auto_devops_enabled? ? _('enabled') : _('disabled')}" }
- %br
- = s_('CICD|Follow the instance default to either have Auto DevOps enabled or disabled when there is no project specific %{ci_file}.').html_safe % { ci_file: ci_file_formatted }
+ .card.auto-devops-card.js-extra-settings{ class: form.object&.enabled == false ? 'hidden' : nil }
+ .card-body.bg-light
+ = form.label :domain do
+ %strong= _('Domain')
+ = form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
+ .form-text.text-muted
+ = s_('CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.')
+ - if cluster_ingress_ip = cluster_ingress_ip(@project)
+ = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "<code>#{cluster_ingress_ip}.nip.io</code>".html_safe }
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank'
- = form.label :domain, class:"prepend-top-10" do
- = _('Domain')
- = form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
- .form-text.text-muted
- = s_('CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages.')
- - if cluster_ingress_ip = cluster_ingress_ip(@project)
- = s_('%{nip_domain} can be used as an alternative to a custom domain.').html_safe % { nip_domain: "<code>#{cluster_ingress_ip}.nip.io</code>".html_safe }
- = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank'
+ %label.prepend-top-10
+ %strong= s_('CICD|Deployment strategy')
+ %p.settings-message.text-center
+ = s_('CICD|Deployment strategy needs a domain name to work correctly.')
+ .form-check
+ = form.radio_button :deploy_strategy, 'continuous', class: 'form-check-input'
+ = form.label :deploy_strategy_continuous, class: 'form-check-label' do
+ %strong= s_('CICD|Continuous deployment to production')
+ = link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-deploy'), target: '_blank'
+ .form-check
+ = form.radio_button :deploy_strategy, 'manual', class: 'form-check-input'
+ = form.label :deploy_strategy_manual, class: 'form-check-label' do
+ %strong= s_('CICD|Automatic deployment to staging, manual deployment to production')
+ = link_to icon('question-circle'), help_page_path('ci/environments.md', anchor: 'manually-deploying-to-environments'), target: '_blank'
+
+ .card.auto-devops-card
+ .card-body
+ .form-check
+ = form.radio_button :enabled, 'false', class: 'form-check-input js-toggle-extra-settings', data: { hide_extra_settings: true }
+ = form.label :enabled_false, class: 'form-check-label' do
+ %strong= s_('CICD|Disable Auto DevOps')
+ .form-text.text-muted
+ = s_('CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery.').html_safe % { ci_file: ci_file_formatted }
= f.submit 'Save changes', class: "btn btn-success prepend-top-15"
diff --git a/app/views/projects/settings/ci_cd/_form.html.haml b/app/views/projects/settings/ci_cd/_form.html.haml
index e93b240a007..7142a9d635e 100644
--- a/app/views/projects/settings/ci_cd/_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_form.html.haml
@@ -7,7 +7,7 @@
= f.label :runners_token, "Runner token", class: 'label-light'
.form-control.js-secret-value-placeholder
= '*' * 20
- = f.text_field :runners_token, class: "form-control hidden js-secret-value", placeholder: 'xEeFCaDAB89'
+ = f.text_field :runners_token, class: "form-control hide js-secret-value", placeholder: 'xEeFCaDAB89'
%p.form-text.text-muted The secure token used by the Runner to checkout the project
%button.btn.btn-info.prepend-top-10.js-secret-value-reveal-button{ type: 'button', data: { secret_reveal_status: 'false' } }
= _('Reveal value')
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index ed118d1bcef..56c175f5649 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -8,7 +8,7 @@
%section.settings#js-general-pipeline-settings.no-animate{ class: ('expanded' if general_expanded) }
.settings-header
%h4
- General pipelines settings
+ General pipelines
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
@@ -16,7 +16,7 @@
.settings-content
= render 'form'
-%section.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-autodevops-settings.settings#autodevops-settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
= s_('CICD|Auto DevOps')
@@ -28,10 +28,10 @@
.settings-content
= render 'autodevops_form'
-%section.settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-runners-settings.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
- Runners settings
+ Runners
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
%p
@@ -39,7 +39,7 @@
.settings-content
= render 'projects/runners/index'
-%section.settings.no-animate{ class: ('expanded' if expanded) }
+%section.qa-variables-settings.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Variables')
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index b596748ca5f..da822ac5675 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -36,7 +36,7 @@
= label_tag :release_description, s_('TagsPage|Release notes'), class: 'col-form-label col-sm-2'
.col-sm-10
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
- = render 'projects/zen', attr: :release_description, classes: 'note-textarea', placeholder: s_('TagsPage|Write your release notes or drag files here...'), current_text: @release_description
+ = render 'projects/zen', attr: :release_description, classes: 'note-textarea', placeholder: s_('TagsPage|Write your release notes or drag files here…'), current_text: @release_description
= render 'shared/notes/hints'
.form-text.text-muted
= s_('TagsPage|Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.')
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index bcceb69954a..26fe1de31fe 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -25,7 +25,7 @@
.col-sm-12= f.label :content, class: 'control-label-full-width'
.col-sm-12
= render layout: 'projects/md_preview', locals: { url: project_wiki_preview_markdown_path(@project, @page.slug) } do
- = render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: s_("WikiPage|Write your content or drag files here...")
+ = render 'projects/zen', f: f, attr: :content, classes: 'note-textarea', placeholder: s_("WikiPage|Write your content or drag files here…")
= render 'shared/notes/hints'
.clearfix
diff --git a/app/views/search/results/_blob_data.html.haml b/app/views/search/results/_blob_data.html.haml
index 0115be41ff1..143e9f91ca3 100644
--- a/app/views/search/results/_blob_data.html.haml
+++ b/app/views/search/results/_blob_data.html.haml
@@ -3,7 +3,8 @@
.js-file-title.file-title
= link_to blob_link do
%i.fa.fa-file
- = search_blob_title(project, file_name)
+ %strong
+ = search_blob_title(project, file_name)
- if blob.data
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
diff --git a/app/views/search/results/_commit.html.haml b/app/views/search/results/_commit.html.haml
index f34eaf89027..ed5a3badf11 100644
--- a/app/views/search/results/_commit.html.haml
+++ b/app/views/search/results/_commit.html.haml
@@ -1 +1 @@
-= render 'projects/commits/commit', project: @project, commit: commit, ref: nil
+= render 'projects/commits/commit', project: commit.project, commit: commit, ref: nil, show_project_name: @project.nil?
diff --git a/app/views/shared/_field.html.haml b/app/views/shared/_field.html.haml
index 0c8d90d92f5..b89045e726a 100644
--- a/app/views/shared/_field.html.haml
+++ b/app/views/shared/_field.html.haml
@@ -9,11 +9,11 @@
- help = field[:help]
- disabled = disable_fields_service?(@service)
-.form-group
+.form-group.row
- if type == "password" && value.present?
- = form.label name, "Enter new #{title.downcase}", class: "col-form-label"
+ = form.label name, "Enter new #{title.downcase}", class: "col-form-label col-sm-2"
- else
- = form.label name, title, class: "col-form-label"
+ = form.label name, title, class: "col-form-label col-sm-2"
.col-sm-10
- if type == 'text'
= form.text_field name, class: "form-control", placeholder: placeholder, required: required, disabled: disabled
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index ba5b65a209d..5eec7b02b54 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -1,93 +1,70 @@
- label_css_id = dom_id(label)
- status = label_subscription_status(label, @project).inquiry if current_user
- subject = local_assigns[:subject]
+- use_label_priority = local_assigns.fetch(:use_label_priority, false)
+- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false)
- toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user
- show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project)
- show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project)
+- tooltip_title = label_status_tooltip(label, status) if status
%li.label-list-item{ id: label_css_id, data: { id: label.id } }
- = render "shared/label_row", label: label
-
- .d-inline-block.d-sm-none.dropdown
- %button.btn.btn-default.label-options-toggle{ type: 'button', data: { toggle: "dropdown" } }
- Options
- = icon('caret-down')
- .dropdown-menu.dropdown-menu-right
- %ul
- - if show_label_merge_requests_link
- %li
- = link_to_label(label, subject: subject, type: :merge_request) do
- View merge requests
- - if show_label_issues_link
- %li
- = link_to_label(label, subject: subject) do
- View open issues
- - if current_user
- %li.label-subscription
- - if can_subscribe_to_label_in_different_levels?(label)
- %a.js-unsubscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } }
- %span Unsubscribe
- %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } }
- %span Subscribe at project level
- %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } }
- %span Subscribe at group level
- - else
- %a.js-subscribe-button.label-subscribe-button{ role: 'button', href: '#', data: { status: status, url: toggle_subscription_path } }
- %span= label_subscription_toggle_button_text(label, @project)
-
- - if can?(current_user, :admin_label, label)
- %li
- = link_to 'Edit', edit_label_path(label)
- %li
- = link_to 'Delete',
- destroy_label_path(label),
- title: 'Delete',
- method: :delete,
- data: {confirm: 'Remove this label? Are you sure?'},
- class: 'text-danger'
-
- .float-right.d-none.d-sm-none.d-md-block
- - if can?(current_user, :admin_label, label)
- - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
- %button.js-promote-project-label-button.btn.btn-transparent.btn-action.has-tooltip{ title: _('Promote to Group Label'),
- disabled: true,
- type: 'button',
- data: { url: promote_project_label_path(label.project, label),
- label_title: label.title,
- label_color: label.color,
- label_text_color: label.text_color,
- group_name: label.project.group.name,
- target: '#promote-label-modal',
- container: 'body',
- toggle: 'modal' } }
- = sprite_icon('level-up')
- = link_to edit_label_path(label), title: "Edit", class: 'btn btn-transparent btn-action', data: {toggle: "tooltip"} do
- %span.sr-only Edit
- = sprite_icon('pencil')
- %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } }
- = link_to "#", title: "Delete", class: 'btn btn-transparent btn-action remove-row', data: { toggle: "tooltip" } do
- %span.sr-only Delete
- = sprite_icon('remove')
+ = render "shared/label_row", label: label, subject: subject, force_priority: force_priority
+ %ul.label-actions-list
+ - if @project
+ %li.inline
+ .label-badge.label-badge-gray= label.model_name.human.capitalize
+ - if can?(current_user, :admin_label, @project)
+ %li.inline.js-toggle-priority{ data: { url: remove_priority_project_label_path(@project, label),
+ dom_id: dom_id(label), type: label.type } }
+ %button.label-action.add-priority.btn.btn-transparent.has-tooltip{ title: _('Prioritize'), type: 'button', data: { placement: 'top' }, aria_label: _('Prioritize label') }
+ = sprite_icon('star-o')
+ %button.label-action.remove-priority.btn.btn-transparent.has-tooltip{ title: _('Remove priority'), type: 'button', data: { placement: 'top' }, aria_label: _('Deprioritize label') }
+ = sprite_icon('star')
+ %li.inline
+ = link_to edit_label_path(label), class: 'btn btn-transparent label-action', aria_label: 'Edit label' do
+ = sprite_icon('pencil')
+ %li.inline
+ .dropdown
+ %button{ type: 'button', class: 'btn btn-transparent js-label-options-dropdown label-action', data: { toggle: 'dropdown' }, aria_label: _('Label actions dropdown') }
+ = sprite_icon('ellipsis_v')
+ .dropdown-menu.dropdown-open-left
+ %ul
+ - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
+ %li
+ %button.js-promote-project-label-button.btn.btn-transparent.btn-action{ disabled: true, type: 'button',
+ data: { url: promote_project_label_path(label.project, label),
+ label_title: label.title,
+ label_color: label.color,
+ label_text_color: label.text_color,
+ group_name: label.project.group.name,
+ target: '#promote-label-modal',
+ container: 'body',
+ toggle: 'modal' } }
+ = _('Promote to group label')
+ %li
+ %span{ data: { toggle: 'modal', target: "#modal-delete-label-#{label.id}" } }
+ %button.text-danger.remove-row{ type: 'button' }= _('Delete')
- if current_user
- .label-subscription.inline
+ %li.inline.label-subscription
- if can_subscribe_to_label_in_different_levels?(label)
- %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path } }
- %span Unsubscribe
- = icon('spinner spin', class: 'label-subscribe-button-loading')
-
+ %button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
+ %span= _('Unsubscribe')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
- %button.dropdown-menu-toggle{ type: 'button', 'data-toggle' => 'dropdown' }
- %span Subscribe
- = icon('chevron-down')
- %ul.dropdown-menu
- %li
- %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_project_label_path(@project, label) } }
- Project level
- %a.js-subscribe-button{ class: ('hidden' unless status.unsubscribed?), data: { url: toggle_subscription_group_label_path(label.group, label) } }
- Group level
+ %button.label-subscribe-button.btn.btn-default{ data: { toggle: 'dropdown' } }
+ %span
+ = _('Subscribe')
+ = sprite_icon('chevron-down')
+ .dropdown-menu.dropdown-open-left
+ %ul
+ %li
+ %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_project_label_path(@project, label) } }
+ %span= _('Subscribe at project level')
+ %li
+ %button.js-subscribe-button.js-group-level.label-subscribe-button.btn.btn-default{ class: ('hidden' unless status.unsubscribed?), data: { status: status, url: toggle_subscription_group_label_path(label.group, label) } }
+ %span= _('Subscribe at group level')
- else
- %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ type: 'button', data: { status: status, url: toggle_subscription_path } }
+ %button.js-subscribe-button.label-subscribe-button.btn.btn-default{ data: { status: status, url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%span= label_subscription_toggle_button_text(label, @project)
- = icon('spinner spin', class: 'label-subscribe-button-loading')
= render 'shared/delete_label_modal', label: label
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index f1c1ca9b2c9..0ae3ab8f090 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,30 +1,23 @@
- subject = local_assigns[:subject]
+- force_priority = local_assigns.fetch(:force_priority, false)
- show_label_issues_link = show_label_issuables_link?(label, :issues, project: @project)
- show_label_merge_requests_link = show_label_issuables_link?(label, :merge_requests, project: @project)
-%span.label-row
- - if can?(current_user, :admin_label, @project)
- .draggable-handler
- = icon('bars')
- .js-toggle-priority.toggle-priority{ data: { url: remove_priority_project_label_path(@project, label),
- dom_id: dom_id(label), type: label.type } }
- %button.add-priority.btn.has-tooltip{ title: 'Prioritize', type: 'button', :'data-placement' => 'top' }
- = icon('star-o')
- %button.remove-priority.btn.has-tooltip{ title: 'Remove priority', type: 'button', :'data-placement' => 'top' }
- = icon('star')
- %span.label-name
- = link_to_label(label, subject: @project, tooltip: false)
- - if defined?(@project) && @project.group.present?
- %span.label-type
- = label.model_name.human.titleize
-
- %span.label-description
+.label-name
+ = link_to_label(label, subject: @project, tooltip: false)
+.label-description
+ .append-right-default.prepend-left-default
- if label.description.present?
- .description-text
+ .description-text.append-bottom-10
= markdown_field(label, :description)
- .d-none.d-sm-none.d-md-block
+ %ul.label-links
- if show_label_issues_link
- = link_to_label(label, subject: subject) { 'Issues' }
+ %li.label-link-item.inline
+ = link_to_label(label, subject: subject) { 'Issues' }
- if show_label_merge_requests_link
&middot;
- = link_to_label(label, subject: subject, type: :merge_request) { 'Merge requests' }
+ %li.label-link-item.inline
+ = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') }
+ - if force_priority
+ %li.label-link-item.js-priority-badge.inline.prepend-left-10
+ .label-badge.label-badge-blue= _('Prioritized label')
diff --git a/app/views/shared/_new_merge_request_checkbox.html.haml b/app/views/shared/_new_merge_request_checkbox.html.haml
index 165109b6b70..24c0dfe247f 100644
--- a/app/views/shared/_new_merge_request_checkbox.html.haml
+++ b/app/views/shared/_new_merge_request_checkbox.html.haml
@@ -1,4 +1,4 @@
-.form-check
+.form-check.prepend-top-8
- nonce = SecureRandom.hex
= check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request form-check-input', id: "create_merge_request-#{nonce}"
= label_tag "create_merge_request-#{nonce}", class: 'form-check-label' do
diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml
index 0ebf365c7bd..6fa61c15493 100644
--- a/app/views/shared/_service_settings.html.haml
+++ b/app/views/shared/_service_settings.html.haml
@@ -3,8 +3,9 @@
- if lookup_context.template_exists?('help', "projects/services/#{@service.to_param}", true)
= render "projects/services/#{@service.to_param}/help", subject: subject
- elsif @service.help.present?
- .card
- = markdown @service.help
+ .info-well
+ .well-segment
+ = markdown @service.help
.service-settings
- if @service.show_active_box?
@@ -15,25 +16,24 @@
- if @service.configurable_events.present?
.form-group.row
- = form.label :url, "Trigger", class: 'col-form-label col-sm-2'
+ .col-sm-2.text-right Trigger
.col-sm-10
- @service.configurable_events.each do |event|
- %div
- = form.check_box service_event_field_name(event), class: 'float-left'
- .prepend-left-20
- = form.label service_event_field_name(event), class: 'list-label' do
+ .form-group
+ .form-check
+ = form.check_box service_event_field_name(event), class: 'form-check-input'
+ = form.label service_event_field_name(event), class: 'form-check-label' do
%strong
= event.humanize
- - field = @service.event_field(event)
+ - field = @service.event_field(event)
- - if field
- %p
+ - if field
= form.text_field field[:name], class: "form-control", placeholder: field[:placeholder]
- %p.light
- = @service.class.event_description(event)
+ %p.text-muted
+ = @service.class.event_description(event)
- @service.global_fields.each do |field|
- type = field[:type]
diff --git a/app/views/shared/boards/components/_board.html.haml b/app/views/shared/boards/components/_board.html.haml
index 67476a3f573..76843ce7cc0 100644
--- a/app/views/shared/boards/components/_board.html.haml
+++ b/app/views/shared/boards/components/_board.html.haml
@@ -1,4 +1,4 @@
-.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded }',
+.board{ ":class" => '{ "is-draggable": !list.preset, "is-expandable": list.isExpandable, "is-collapsed": !list.isExpanded, "board-type-assignee": list.type === "assignee" }',
":data-id" => "list.id" }
.board-inner
%header.board-header{ ":class" => '{ "has-border": list.label && list.label.color }', ":style" => "{ borderTopColor: (list.label && list.label.color ? list.label.color : null) }", "@click" => "toggleExpanded($event)" }
@@ -7,10 +7,18 @@
":class": "{ \"fa-caret-down\": list.isExpanded, \"fa-caret-right\": !list.isExpanded }",
"aria-hidden": "true" }
+ %a.user-avatar-link.js-no-trigger{ "v-if": "list.type === \"assignee\"", ":href": "list.assignee.path" }
+ -# haml-lint:disable AltText
+ %img.avatar.s20.has-tooltip{ height: "20", width: "20", ":src": "list.assignee.avatar", ":alt": "list.assignee.name" }
+
%span.board-title-text.has-tooltip{ "v-if": "list.type !== \"label\"",
- ":title" => '(list.label ? list.label.description : "")', data: { container: "body" } }
+ ":title" => '((list.label && list.label.description) || list.title || "")', data: { container: "body" } }
{{ list.title }}
+ %span.board-title-sub-text.prepend-left-5.has-tooltip{ "v-if": "list.type === \"assignee\"",
+ ":title" => '(list.assignee && list.assignee.username || "")' }
+ @{{ list.assignee.username }}
+
%span.has-tooltip{ "v-if": "list.type === \"label\"",
":title" => '(list.label ? list.label.description : "")',
data: { container: "body", placement: "bottom" },
diff --git a/app/views/shared/empty_states/_wikis.html.haml b/app/views/shared/empty_states/_wikis.html.haml
index fabb1f39a34..f1a41074c28 100644
--- a/app/views/shared/empty_states/_wikis.html.haml
+++ b/app/views/shared/empty_states/_wikis.html.haml
@@ -8,7 +8,7 @@
%h4
= s_('WikiEmpty|The wiki lets you write documentation for your project')
%p.text-left
- = s_("WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, it's principles, how to use it, and so on.")
+ = s_("WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on.")
= create_link
- elsif can?(current_user, :read_issue, @project)
diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml
index e5dfa7dbf71..25df2fe5cd6 100644
--- a/app/views/shared/form_elements/_description.html.haml
+++ b/app/views/shared/form_elements/_description.html.haml
@@ -16,7 +16,7 @@
= render layout: 'projects/md_preview', locals: { url: preview_url, referenced_users: true } do
= render 'projects/zen', f: form, attr: :description,
classes: 'note-textarea qa-issuable-form-description',
- placeholder: "Write a comment or drag your files here...",
+ placeholder: "Write a comment or drag your files here…",
supports_quick_actions: supports_quick_actions
= render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
.clearfix
diff --git a/app/views/shared/issuable/_board_create_list_dropdown.html.haml b/app/views/shared/issuable/_board_create_list_dropdown.html.haml
new file mode 100644
index 00000000000..23b2e1b91e5
--- /dev/null
+++ b/app/views/shared/issuable/_board_create_list_dropdown.html.haml
@@ -0,0 +1,8 @@
+.dropdown.prepend-left-10#js-add-list
+ %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
+ Add list
+ .dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable.js-tab-container-labels
+ = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
+ - if can?(current_user, :admin_label, board.parent)
+ = render partial: "shared/issuable/label_page_create"
+ = dropdown_loading
diff --git a/app/views/shared/issuable/_label_page_create.html.haml b/app/views/shared/issuable/_label_page_create.html.haml
index 9f4021802df..55edaa7eda4 100644
--- a/app/views/shared/issuable/_label_page_create.html.haml
+++ b/app/views/shared/issuable/_label_page_create.html.haml
@@ -1,6 +1,7 @@
+- show_close = local_assigns.fetch(:show_close, true)
- subject = @project || @group
.dropdown-page-two.dropdown-new-label
- = dropdown_title(create_label_title(subject), options: { back: true })
+ = dropdown_title(create_label_title(subject), options: { back: true, close: show_close })
= dropdown_content do
.dropdown-labels-error.js-label-error
%input#new_label_name.default-dropdown-input{ type: "text", placeholder: _('Name new label') }
diff --git a/app/views/shared/issuable/_label_page_default.html.haml b/app/views/shared/issuable/_label_page_default.html.haml
index 2bd922bca2b..aa4a5f0e0d3 100644
--- a/app/views/shared/issuable/_label_page_default.html.haml
+++ b/app/views/shared/issuable/_label_page_default.html.haml
@@ -1,15 +1,18 @@
- title = local_assigns.fetch(:title, _('Assign labels'))
+- content_title = local_assigns.fetch(:content_title, _('Create lists from labels. Issues with that label appear in that list.'))
+- show_title = local_assigns.fetch(:show_title, true)
- show_create = local_assigns.fetch(:show_create, true)
- show_footer = local_assigns.fetch(:show_footer, true)
- filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search')
- show_boards_content = local_assigns.fetch(:show_boards_content, false)
- subject = @project || @group
.dropdown-page-one
- = dropdown_title(title)
+ - if show_title
+ = dropdown_title(title)
- if show_boards_content
.issue-board-dropdown-content
%p
- = _('Create lists from labels. Issues with that label appear in that list.')
+ = content_title
= dropdown_filter(filter_placeholder)
= dropdown_content
- if current_board_parent && show_footer
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 644f7c4dd28..ef9ea2194ee 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -104,14 +104,7 @@
.filter-dropdown-container
- if type == :boards
- if can?(current_user, :admin_list, board.parent)
- .dropdown.prepend-left-10#js-add-list
- %button.btn.btn-create.btn-inverted.js-new-board-list{ type: "button", data: board_list_data }
- Add list
- .dropdown-menu.dropdown-menu-paging.dropdown-menu-right.dropdown-menu-issues-board-new.dropdown-menu-selectable
- = render partial: "shared/issuable/label_page_default", locals: { show_footer: true, show_create: true, show_boards_content: true, title: "Add list" }
- - if can?(current_user, :admin_label, board.parent)
- = render partial: "shared/issuable/label_page_create"
- = dropdown_loading
+ = render_if_exists 'shared/issuable/board_create_list_dropdown', board: board
- if @project
#js-add-issues-btn.prepend-left-10{ data: { can_admin_list: can?(current_user, :admin_list, @project) } }
- elsif type != :boards_modal
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 9e50e888b35..0ca35ea1298 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -43,7 +43,7 @@
.selectbox.hide-collapsed
= f.hidden_field 'milestone_id', value: issuable.milestone_id, id: nil
- = dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: project_milestones_path(@project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true, default_no: true, selected: (issuable.milestone.name if issuable.milestone), null_default: true }})
+ = dropdown_tag('Milestone', options: { title: _('Assign milestone'), toggle_class: 'js-milestone-select js-extra-options', filter: true, dropdown_class: 'dropdown-menu-selectable', placeholder: _('Search milestones'), data: { show_no: true, field_name: "#{issuable.to_ability_name}[milestone_id]", project_id: @project.id, issuable_id: issuable.id, milestones: project_milestones_path(@project, :json), ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), use_id: true, default_no: true, selected: (issuable.milestone.name if issuable.milestone), null_default: true, display: 'static' }})
- if issuable.has_attribute?(:time_estimate)
#issuable-time-tracker.block
// Fallback while content is loading
@@ -77,7 +77,7 @@
.selectbox.hide-collapsed
= f.hidden_field :due_date, value: issuable.due_date.try(:strftime, 'yy-mm-dd')
.dropdown
- %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable) } }
+ %button.dropdown-menu-toggle.js-due-date-select{ type: 'button', data: { toggle: 'dropdown', field_name: "#{issuable.to_ability_name}[due_date]", ability_name: issuable.to_ability_name, issue_update: issuable_json_path(issuable), display: 'static' } }
%span.dropdown-toggle-text
= _('Due date')
= icon('chevron-down', 'aria-hidden': 'true')
@@ -109,7 +109,7 @@
- selected_labels.each do |label|
= hidden_field_tag "#{issuable.to_ability_name}[label_names][]", label.id, id: nil
.dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path(false) if @project) } }
+ %button.dropdown-menu-toggle.js-label-select.js-multiselect.js-label-sidebar-dropdown{ type: "button", data: {toggle: "dropdown", default_label: "Labels", field_name: "#{issuable.to_ability_name}[label_names][]", ability_name: issuable.to_ability_name, show_no: "true", show_any: "true", namespace_path: @project.try(:namespace).try(:full_path), project_path: @project.try(:path), issue_update: issuable_json_path(issuable), labels: (labels_filter_path(false) if @project), display: 'static' } }
%span.dropdown-toggle-text{ class: ("is-default" if selected_labels.empty?) }
= multi_label_name(selected_labels, "Labels")
= icon('chevron-down', 'aria-hidden': 'true')
diff --git a/app/views/shared/issuable/_sidebar_assignees.html.haml b/app/views/shared/issuable/_sidebar_assignees.html.haml
index e1cde527ad7..ed3ef6155db 100644
--- a/app/views/shared/issuable/_sidebar_assignees.html.haml
+++ b/app/views/shared/issuable/_sidebar_assignees.html.haml
@@ -37,7 +37,7 @@
- issuable.assignees.each do |assignee|
= hidden_field_tag "#{issuable.to_ability_name}[assignee_ids][]", assignee.id, id: nil, data: { avatar_url: assignee.avatar_url, name: assignee.name, username: assignee.username }
- - options = { toggle_class: 'js-user-search js-author-search', title: _('Assign to'), filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: _('Search users'), data: { first_user: current_user&.username, current_user: true, project_id: @project&.id, author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true } }
+ - options = { toggle_class: 'js-user-search js-author-search', title: _('Assign to'), filter: true, dropdown_class: 'dropdown-menu-user dropdown-menu-selectable dropdown-menu-author', placeholder: _('Search users'), data: { first_user: current_user&.username, current_user: true, project_id: @project&.id, author_id: issuable.author_id, field_name: "#{issuable.to_ability_name}[assignee_ids][]", issue_update: issuable_json_path(issuable), ability_name: issuable.to_ability_name, null_user: true, display: 'static' } }
- title = _('Select assignee')
- if issuable.is_a?(Issue)
diff --git a/app/views/shared/issuable/form/_contribution.html.haml b/app/views/shared/issuable/form/_contribution.html.haml
index 519b5fae846..bc9a1edc39c 100644
--- a/app/views/shared/issuable/form/_contribution.html.haml
+++ b/app/views/shared/issuable/form/_contribution.html.haml
@@ -7,11 +7,11 @@
%hr
-.form-group
- .col-form-label
+.form-group.row
+ %label.col-form-label.col-sm-2
= _('Contribution')
.col-sm-10
- .form-check
+ .form-check.prepend-top-5
= form.check_box :allow_collaboration, disabled: !issuable.can_allow_collaboration?(current_user), class: 'form-check-input'
= form.label :allow_collaboration, class: 'form-check-label' do
= _('Allow commits from members who can merge to the target branch.')
diff --git a/app/views/shared/members/_group.html.haml b/app/views/shared/members/_group.html.haml
index 67b8843a27f..d0b492b43f3 100644
--- a/app/views/shared/members/_group.html.haml
+++ b/app/views/shared/members/_group.html.haml
@@ -5,16 +5,16 @@
%li.member.group_member{ id: dom_id }
%span.list-item-name
= group_icon(group, class: "avatar s40", alt: '')
- %strong
- = link_to group.full_name, group_path(group)
- .cgray
- Given access #{time_ago_with_tooltip(group_link.created_at)}
- - if group_link.expires?
- ·
- %span{ class: ('text-warning' if group_link.expires_soon?) }
- Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
+ .user-info
+ = link_to group.full_name, group_path(group), class: 'member'
+ .cgray
+ Given access #{time_ago_with_tooltip(group_link.created_at)}
+ - if group_link.expires?
+ ·
+ %span{ class: ('text-warning' if group_link.expires_soon?) }
+ Expires in #{distance_of_time_in_words_to_now(group_link.expires_at)}
.controls.member-controls
- = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form' do
+ = form_tag project_group_link_path(@project, group_link), method: :put, remote: true, class: 'js-edit-member-form form-group row append-right-5' do
= hidden_field_tag "group_link[group_access]", group_link.group_access
.member-form-control.dropdown.append-right-5
%button.dropdown-menu-toggle.js-member-permissions-dropdown{ type: "button",
diff --git a/app/views/shared/notes/_edit_form.html.haml b/app/views/shared/notes/_edit_form.html.haml
index 8923e5602a4..71a5b94e958 100644
--- a/app/views/shared/notes/_edit_form.html.haml
+++ b/app/views/shared/notes/_edit_form.html.haml
@@ -3,7 +3,7 @@
= hidden_field_tag :target_id, '', class: 'js-form-target-id'
= hidden_field_tag :target_type, '', class: 'js-form-target-type'
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(project), referenced_users: true } do
- = render 'projects/zen', attr: 'note[note]', classes: 'note-textarea js-note-text js-task-list-field', placeholder: "Write a comment or drag your files here..."
+ = render 'projects/zen', attr: 'note[note]', classes: 'note-textarea js-note-text js-task-list-field', placeholder: "Write a comment or drag your files here…"
= render 'shared/notes/hints'
.note-form-actions.clearfix
diff --git a/app/views/shared/notes/_form.html.haml b/app/views/shared/notes/_form.html.haml
index 71c0d740bc8..c360f1ffe2a 100644
--- a/app/views/shared/notes/_form.html.haml
+++ b/app/views/shared/notes/_form.html.haml
@@ -29,7 +29,7 @@
= render 'projects/zen', f: f,
attr: :note,
classes: 'note-textarea js-note-text',
- placeholder: "Write a comment or drag your files here...",
+ placeholder: "Write a comment or drag your files here…",
supports_quick_actions: supports_quick_actions,
supports_autocomplete: supports_autocomplete
= render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
diff --git a/app/views/shared/tokens/_scopes_form.html.haml b/app/views/shared/tokens/_scopes_form.html.haml
index ae437dd16d6..2d0bb722189 100644
--- a/app/views/shared/tokens/_scopes_form.html.haml
+++ b/app/views/shared/tokens/_scopes_form.html.haml
@@ -5,6 +5,6 @@
- scopes.each do |scope|
%fieldset
= check_box_tag "#{prefix}[scopes][]", scope, token.scopes.include?(scope), id: "#{prefix}_scopes_#{scope}"
- = label_tag ("#{prefix}_scopes_#{scope}"), scope
+ = label_tag ("#{prefix}_scopes_#{scope}"), scope, class: "label-light"
%span= t(scope, scope: [:doorkeeper, :scopes])
.scope-description= t scope, scope: [:doorkeeper, :scope_desc]
diff --git a/app/views/users/terms/index.html.haml b/app/views/users/terms/index.html.haml
index 33cddf63952..a869cf9cdee 100644
--- a/app/views/users/terms/index.html.haml
+++ b/app/views/users/terms/index.html.haml
@@ -2,16 +2,17 @@
.card-body.rendered-terms
= markdown_field(@term, :terms)
-.card-footer.footer-block.clearfix
- - if can?(current_user, :accept_terms, @term)
- .float-right
- = button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
- = _('Accept terms')
- - else
- .pull-right
- = link_to root_path, class: 'btn btn-success prepend-left-8' do
- = _('Continue')
- - if can?(current_user, :decline_terms, @term)
- .float-right
- = button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
- = _('Decline and sign out')
+- if current_user
+ .card-footer.footer-block.clearfix
+ - if can?(current_user, :accept_terms, @term)
+ .float-right
+ = button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do
+ = _('Accept terms')
+ - else
+ .pull-right
+ = link_to root_path, class: 'btn btn-success prepend-left-8' do
+ = _('Continue')
+ - if can?(current_user, :decline_terms, @term)
+ .float-right
+ = button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do
+ = _('Decline and sign out')
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index f3c9e2b1582..ae5c5fac834 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -6,12 +6,6 @@ class GitGarbageCollectWorker
# Timeout set to 24h
LEASE_TIMEOUT = 86400
- GITALY_MIGRATED_TASKS = {
- gc: :garbage_collect,
- full_repack: :repack_full,
- incremental_repack: :repack_incremental
- }.freeze
-
def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
project = Project.find(project_id)
active_uuid = get_lease_uuid(lease_key)
@@ -27,21 +21,7 @@ class GitGarbageCollectWorker
end
task = task.to_sym
- cmd = command(task)
-
- gitaly_migrate(GITALY_MIGRATED_TASKS[task], status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_call(task, project.repository.raw_repository)
- else
- repo_path = project.repository.path_to_repo
- description = "'#{cmd.join(' ')}' in #{repo_path}"
- Gitlab::GitLogger.info(description)
-
- output, status = Gitlab::Popen.popen(cmd, repo_path)
-
- Gitlab::GitLogger.error("#{description} failed:\n#{output}") unless status.zero?
- end
- end
+ gitaly_call(task, project.repository.raw_repository)
# Refresh the branch cache in case garbage collection caused a ref lookup to fail
flush_ref_caches(project) if task == :gc
@@ -82,21 +62,12 @@ class GitGarbageCollectWorker
when :incremental_repack
client.repack_incremental
end
- end
-
- def command(task)
- case task
- when :gc
- git(write_bitmaps: bitmaps_enabled?) + %w[gc]
- when :full_repack
- git(write_bitmaps: bitmaps_enabled?) + %w[repack -A -d --pack-kept-objects]
- when :incremental_repack
- # Normal git repack fails when bitmaps are enabled. It is impossible to
- # create a bitmap here anyway.
- git(write_bitmaps: false) + %w[repack -d]
- else
- raise "Invalid gc task: #{task.inspect}"
- end
+ rescue GRPC::NotFound => e
+ Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
+ raise Gitlab::Git::Repository::NoRepository.new(e)
+ rescue GRPC::BadStatus => e
+ Gitlab::GitLogger.error("#{method} failed:\n#{e}")
+ raise Gitlab::Git::CommandError.new(e)
end
def flush_ref_caches(project)
@@ -108,19 +79,4 @@ class GitGarbageCollectWorker
def bitmaps_enabled?
Gitlab::CurrentSettings.housekeeping_bitmaps_enabled
end
-
- def git(write_bitmaps:)
- config_value = write_bitmaps ? 'true' : 'false'
- %W[git -c repack.writeBitmaps=#{config_value}]
- end
-
- def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
- Gitlab::GitalyClient.migrate(method, status: status, &block)
- rescue GRPC::NotFound => e
- Gitlab::GitLogger.error("#{method} failed:\nRepository not found")
- raise Gitlab::Git::Repository::NoRepository.new(e)
- rescue GRPC::BadStatus => e
- Gitlab::GitLogger.error("#{method} failed:\n#{e}")
- raise Gitlab::Git::CommandError.new(e)
- end
end
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index 08b1c3a7d7a..db48bb7e8b8 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -23,10 +23,11 @@ class RepositoryForkWorker
source_repository_storage_path, source_disk_path = *args
- source_repository_storage_name = Gitlab.config.repositories.storages.find do |_, info|
- info.legacy_disk_path == source_repository_storage_path
- end&.first || raise("no shard found for path '#{source_repository_storage_path}'")
-
+ source_repository_storage_name = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.find do |_, info|
+ info.legacy_disk_path == source_repository_storage_path
+ end&.first || raise("no shard found for path '#{source_repository_storage_path}'")
+ end
fork_repository(target_project, source_repository_storage_name, source_disk_path)
end
end
diff --git a/app/workers/storage_migrator_worker.rb b/app/workers/storage_migrator_worker.rb
index f92421a667d..0aff0c4c7c6 100644
--- a/app/workers/storage_migrator_worker.rb
+++ b/app/workers/storage_migrator_worker.rb
@@ -1,29 +1,8 @@
class StorageMigratorWorker
include ApplicationWorker
- BATCH_SIZE = 100
-
def perform(start, finish)
- projects = build_relation(start, finish)
-
- projects.with_route.find_each(batch_size: BATCH_SIZE) do |project|
- Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..."
-
- begin
- project.migrate_to_hashed_storage!
- rescue => err
- Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
- end
- end
- end
-
- def build_relation(start, finish)
- relation = Project
- table = Project.arel_table
-
- relation = relation.where(table[:id].gteq(start)) if start
- relation = relation.where(table[:id].lteq(finish)) if finish
-
- relation
+ migrator = Gitlab::HashedStorage::Migrator.new
+ migrator.bulk_migrate(start, finish)
end
end
diff --git a/changelogs/unreleased/35158-snippets-api-visibility.yml b/changelogs/unreleased/35158-snippets-api-visibility.yml
new file mode 100644
index 00000000000..f06015dda46
--- /dev/null
+++ b/changelogs/unreleased/35158-snippets-api-visibility.yml
@@ -0,0 +1,5 @@
+---
+title: Expose visibility via Snippets API
+merge_request: 19620
+author: Jan Beckmann
+type: added
diff --git a/changelogs/unreleased/38542-application-control-panel-in-settings-page.yml b/changelogs/unreleased/38542-application-control-panel-in-settings-page.yml
new file mode 100644
index 00000000000..0654456ea45
--- /dev/null
+++ b/changelogs/unreleased/38542-application-control-panel-in-settings-page.yml
@@ -0,0 +1,5 @@
+---
+title: Add deploy strategies to the Auto DevOps settings
+merge_request: 19172
+author:
+type: added
diff --git a/changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml b/changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml
new file mode 100644
index 00000000000..fb4fbf80575
--- /dev/null
+++ b/changelogs/unreleased/39549-label-list-page-redesign-with-draggable-labels.yml
@@ -0,0 +1,5 @@
+---
+title: Label list page redesign
+merge_request: 18466
+author:
+type: changed
diff --git a/changelogs/unreleased/42342-teams-pipeline-notifications.yml b/changelogs/unreleased/42342-teams-pipeline-notifications.yml
new file mode 100644
index 00000000000..4ef3a35465b
--- /dev/null
+++ b/changelogs/unreleased/42342-teams-pipeline-notifications.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes Microsoft Teams notifications for pipeline events
+merge_request: 19632
+author: Jeff Brown
+type: fixed
diff --git a/changelogs/unreleased/43597-new-navigation-themes.yml b/changelogs/unreleased/43597-new-navigation-themes.yml
new file mode 100644
index 00000000000..de703e46b3c
--- /dev/null
+++ b/changelogs/unreleased/43597-new-navigation-themes.yml
@@ -0,0 +1,5 @@
+---
+title: Add additional theme color options
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/44267-improve-failed-jobs-tab.yml b/changelogs/unreleased/44267-improve-failed-jobs-tab.yml
new file mode 100644
index 00000000000..9743704e23d
--- /dev/null
+++ b/changelogs/unreleased/44267-improve-failed-jobs-tab.yml
@@ -0,0 +1,5 @@
+---
+title: Improve Failed Jobs tab in the Pipeline detail page
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml b/changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml
new file mode 100644
index 00000000000..69733889d5a
--- /dev/null
+++ b/changelogs/unreleased/44674-use-one-column-form-layout-on-admin-area-settings-page.yml
@@ -0,0 +1,5 @@
+---
+title: Use one column form layout on Admin Area Settings page
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/44790-disabled-emails-logging.yml b/changelogs/unreleased/44790-disabled-emails-logging.yml
new file mode 100644
index 00000000000..90125dc0300
--- /dev/null
+++ b/changelogs/unreleased/44790-disabled-emails-logging.yml
@@ -0,0 +1,5 @@
+---
+title: Stop logging email information when emails are disabled
+merge_request: 18521
+author: Marc Shaw
+type: fixed
diff --git a/changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml b/changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml
new file mode 100644
index 00000000000..5aba62435ed
--- /dev/null
+++ b/changelogs/unreleased/45400-automatically-created-mr-uses-wrong-target-branch-when-branching-from-tag.yml
@@ -0,0 +1,5 @@
+---
+title: Set MR target branch to default branch if target branch is not valid
+merge_request: 19067
+author:
+type: fixed
diff --git a/changelogs/unreleased/45505-lograge_formatter_encoding.yml b/changelogs/unreleased/45505-lograge_formatter_encoding.yml
new file mode 100644
index 00000000000..02f4c152966
--- /dev/null
+++ b/changelogs/unreleased/45505-lograge_formatter_encoding.yml
@@ -0,0 +1,6 @@
+---
+title: Enforce UTF-8 encoding on user input in LogrageWithTimestamp formatter and
+ filter out file content from logs
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/45575-invalid-characters-signup.yml b/changelogs/unreleased/45575-invalid-characters-signup.yml
new file mode 100644
index 00000000000..679bd13e59b
--- /dev/null
+++ b/changelogs/unreleased/45575-invalid-characters-signup.yml
@@ -0,0 +1,5 @@
+---
+title: 'Fix username validation order on signup, resolves #45575'
+merge_request: 19610
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml b/changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml
new file mode 100644
index 00000000000..6974be07716
--- /dev/null
+++ b/changelogs/unreleased/46075-automatically-provide-deploy-token-when-autodevops-is-enabled.yml
@@ -0,0 +1,5 @@
+---
+title: Automatize Deploy Token creation for Auto Devops
+merge_request: 19507
+author:
+type: added
diff --git a/changelogs/unreleased/46648-timeout-searching-group-issues.yml b/changelogs/unreleased/46648-timeout-searching-group-issues.yml
new file mode 100644
index 00000000000..54401edf5cc
--- /dev/null
+++ b/changelogs/unreleased/46648-timeout-searching-group-issues.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance of group issues filtering on GitLab.com
+merge_request: 19429
+author:
+type: performance
diff --git a/changelogs/unreleased/46922-hashed-storage-single-project.yml b/changelogs/unreleased/46922-hashed-storage-single-project.yml
new file mode 100644
index 00000000000..c293238a5a4
--- /dev/null
+++ b/changelogs/unreleased/46922-hashed-storage-single-project.yml
@@ -0,0 +1,5 @@
+---
+title: 'Hashed Storage: migration rake task now can be executed to specific project'
+merge_request: 19268
+author:
+type: changed
diff --git a/changelogs/unreleased/47050-quick-actions-case-insensitive.yml b/changelogs/unreleased/47050-quick-actions-case-insensitive.yml
new file mode 100644
index 00000000000..176aba627b9
--- /dev/null
+++ b/changelogs/unreleased/47050-quick-actions-case-insensitive.yml
@@ -0,0 +1,5 @@
+---
+title: Make quick commands case insensitive
+merge_request: 19614
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/47145-quick-actions-confidential.yml b/changelogs/unreleased/47145-quick-actions-confidential.yml
new file mode 100644
index 00000000000..7ae4e2268af
--- /dev/null
+++ b/changelogs/unreleased/47145-quick-actions-confidential.yml
@@ -0,0 +1,5 @@
+---
+title: Add /confidential quick action
+merge_request:
+author: Jan Beckmann
+type: added
diff --git a/changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml b/changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml
new file mode 100644
index 00000000000..010b1db5aac
--- /dev/null
+++ b/changelogs/unreleased/47182-use-the-default-strings-of-timeago-js.yml
@@ -0,0 +1,5 @@
+---
+title: Use the default strings of timeago.js for timeago
+merge_request: 19350
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/47208-human-import-status-name-not-working.yml b/changelogs/unreleased/47208-human-import-status-name-not-working.yml
new file mode 100644
index 00000000000..e1f603f988e
--- /dev/null
+++ b/changelogs/unreleased/47208-human-import-status-name-not-working.yml
@@ -0,0 +1,5 @@
+---
+title: Showing project import_status in a humanized form no longer gives an error
+merge_request: 19470
+author:
+type: fixed
diff --git a/changelogs/unreleased/47408-migrateuploadsworker-is-doing-n-1-queries-on-migration.yml b/changelogs/unreleased/47408-migrateuploadsworker-is-doing-n-1-queries-on-migration.yml
new file mode 100644
index 00000000000..c0df82f35f1
--- /dev/null
+++ b/changelogs/unreleased/47408-migrateuploadsworker-is-doing-n-1-queries-on-migration.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize the upload migration proces
+merge_request: 15947
+author:
+type: fixed
diff --git a/changelogs/unreleased/47513-upload-migration-lease-key-is-incorrect-for-non-mounted-uploaders.yml b/changelogs/unreleased/47513-upload-migration-lease-key-is-incorrect-for-non-mounted-uploaders.yml
new file mode 100644
index 00000000000..010c4e9bce7
--- /dev/null
+++ b/changelogs/unreleased/47513-upload-migration-lease-key-is-incorrect-for-non-mounted-uploaders.yml
@@ -0,0 +1,5 @@
+---
+title: Use upload ID for creating lease key for file uploaders.
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/47604-avatars-and-system-icons-for-mobile.yml b/changelogs/unreleased/47604-avatars-and-system-icons-for-mobile.yml
new file mode 100644
index 00000000000..ff66385375f
--- /dev/null
+++ b/changelogs/unreleased/47604-avatars-and-system-icons-for-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Make avatars/icons hidden on mobile
+merge_request: 19585
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/47679-fix-failed-jobs-tab-ie11.yml b/changelogs/unreleased/47679-fix-failed-jobs-tab-ie11.yml
new file mode 100644
index 00000000000..48f3bc87eee
--- /dev/null
+++ b/changelogs/unreleased/47679-fix-failed-jobs-tab-ie11.yml
@@ -0,0 +1,5 @@
+---
+title: Fix overflowing Failed Jobs table in sm viewports on IE11
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml b/changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml
new file mode 100644
index 00000000000..86680b6b117
--- /dev/null
+++ b/changelogs/unreleased/add-new-arg-to-git-rev-list-call.yml
@@ -0,0 +1,5 @@
+---
+title: Improve performance of LFS integrity check
+merge_request: 19494
+author:
+type: performance
diff --git a/changelogs/unreleased/author-doc-fix.yml b/changelogs/unreleased/author-doc-fix.yml
new file mode 100644
index 00000000000..83521543239
--- /dev/null
+++ b/changelogs/unreleased/author-doc-fix.yml
@@ -0,0 +1,5 @@
+---
+title: Fix fields for author & assignee in MR API docs.
+merge_request: 19798
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml b/changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml
new file mode 100644
index 00000000000..69d49f3e3e0
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-add-gemfile-rails5-lock-check.yml
@@ -0,0 +1,5 @@
+---
+title: Add CI job to check Gemfile.rails5.lock
+merge_request: 19605
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml b/changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml
new file mode 100644
index 00000000000..9d975ff81bf
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-bump-grape-path-helpers-gem-to-1-0-5.yml
@@ -0,0 +1,5 @@
+---
+title: Bump grape-path-helpers to 1.0.5
+merge_request: 19604
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml b/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml
new file mode 100644
index 00000000000..597b85de26f
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-rails5-fix-snippets-finder.yml
@@ -0,0 +1,5 @@
+---
+title: "[Rails5] Fix snippets_finder arel queries"
+merge_request: 19796
+author: "@blackst0ne"
+type: fixed
diff --git a/changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml b/changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml
new file mode 100644
index 00000000000..54154ad2449
--- /dev/null
+++ b/changelogs/unreleased/bvl-fix-maintainer-push-rejected.yml
@@ -0,0 +1,6 @@
+---
+title: Fix bug where maintainer would not be allowed to push to forks with merge requests
+ that have `Allow maintainer edits` enabled.
+merge_request: 18968
+author:
+type: fixed
diff --git a/changelogs/unreleased/bvl-terms-on-registration.yml b/changelogs/unreleased/bvl-terms-on-registration.yml
new file mode 100644
index 00000000000..3e6e499dd02
--- /dev/null
+++ b/changelogs/unreleased/bvl-terms-on-registration.yml
@@ -0,0 +1,5 @@
+---
+title: Users can accept terms during registration
+merge_request: 19583
+author:
+type: other
diff --git a/changelogs/unreleased/bw-enable-commonmark.yml b/changelogs/unreleased/bw-enable-commonmark.yml
new file mode 100644
index 00000000000..89252e5063d
--- /dev/null
+++ b/changelogs/unreleased/bw-enable-commonmark.yml
@@ -0,0 +1,5 @@
+---
+title: Use CommonMark syntax and rendering for new Markdown content
+merge_request: 19331
+author:
+type: added
diff --git a/changelogs/unreleased/cache-doc-fix.yml b/changelogs/unreleased/cache-doc-fix.yml
new file mode 100644
index 00000000000..db4726a92e9
--- /dev/null
+++ b/changelogs/unreleased/cache-doc-fix.yml
@@ -0,0 +1,5 @@
+---
+title: 'Remove incorrect CI doc re: PowerShell'
+merge_request: 19622
+author: gfyoung
+type: fixed
diff --git a/changelogs/unreleased/commits_api_with_stats.yml b/changelogs/unreleased/commits_api_with_stats.yml
new file mode 100644
index 00000000000..4357f1a6305
--- /dev/null
+++ b/changelogs/unreleased/commits_api_with_stats.yml
@@ -0,0 +1,5 @@
+---
+title: Added with_statsoption for GET /projects/:id/repository/commits
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/da-port-cte-to-ce.yml b/changelogs/unreleased/da-port-cte-to-ce.yml
new file mode 100644
index 00000000000..6fa759fcf7d
--- /dev/null
+++ b/changelogs/unreleased/da-port-cte-to-ce.yml
@@ -0,0 +1,5 @@
+---
+title: Add Gitlab::SQL:CTE for easily building CTE statements
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/expose-ci-url.yml b/changelogs/unreleased/expose-ci-url.yml
new file mode 100644
index 00000000000..b6ad7d18e0d
--- /dev/null
+++ b/changelogs/unreleased/expose-ci-url.yml
@@ -0,0 +1,5 @@
+---
+title: Add CI_PIPELINE_URL and CI_JOB_URL
+merge_request: 19618
+author:
+type: added
diff --git a/changelogs/unreleased/feature-customizable-favicon.yml b/changelogs/unreleased/feature-customizable-favicon.yml
new file mode 100644
index 00000000000..0e5afc17c9e
--- /dev/null
+++ b/changelogs/unreleased/feature-customizable-favicon.yml
@@ -0,0 +1,5 @@
+---
+title: Allow changing the default favicon to a custom icon.
+merge_request: 14497
+author: Alexis Reigel
+type: added
diff --git a/changelogs/unreleased/fix-br-decode.yml b/changelogs/unreleased/fix-br-decode.yml
new file mode 100644
index 00000000000..66ecc3deb35
--- /dev/null
+++ b/changelogs/unreleased/fix-br-decode.yml
@@ -0,0 +1,5 @@
+---
+title: mergeError message has been binded using v-html directive
+merge_request: 19058
+author: Murat Dogan
+type: fixed
diff --git a/changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml b/changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml
new file mode 100644
index 00000000000..3b4d429707f
--- /dev/null
+++ b/changelogs/unreleased/fj-bumping-gollum-lib-and-gollum-rugged-adapter.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed bug that allowed to remove other wiki pages if the title had wildcard characters
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fj-relax-url-validator-rules-for-user.yml b/changelogs/unreleased/fj-relax-url-validator-rules-for-user.yml
new file mode 100644
index 00000000000..4c72e4c5c1c
--- /dev/null
+++ b/changelogs/unreleased/fj-relax-url-validator-rules-for-user.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid checking the user format in every url validation
+merge_request: 19575
+author:
+type: changed
diff --git a/changelogs/unreleased/fj-restore-users-v3-endpoint.yml b/changelogs/unreleased/fj-restore-users-v3-endpoint.yml
new file mode 100644
index 00000000000..c5f952dfa88
--- /dev/null
+++ b/changelogs/unreleased/fj-restore-users-v3-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Restore API v3 user endpoint
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/gitaly-commit-count-opt-out.yml b/changelogs/unreleased/gitaly-commit-count-opt-out.yml
new file mode 100644
index 00000000000..fd8298b1d7b
--- /dev/null
+++ b/changelogs/unreleased/gitaly-commit-count-opt-out.yml
@@ -0,0 +1,5 @@
+---
+title: Move some Gitaly RPC's to opt-out
+merge_request: 19591
+author:
+type: other
diff --git a/changelogs/unreleased/gitaly-opt-out-branch-tag.yml b/changelogs/unreleased/gitaly-opt-out-branch-tag.yml
new file mode 100644
index 00000000000..750fc863eed
--- /dev/null
+++ b/changelogs/unreleased/gitaly-opt-out-branch-tag.yml
@@ -0,0 +1,5 @@
+---
+title: Move Gitaly branch/tag/ref RPC's to opt-out
+merge_request: 19644
+author:
+type: other
diff --git a/changelogs/unreleased/ide-url-util-relative-url-fix.yml b/changelogs/unreleased/ide-url-util-relative-url-fix.yml
new file mode 100644
index 00000000000..9f0f4a0f7be
--- /dev/null
+++ b/changelogs/unreleased/ide-url-util-relative-url-fix.yml
@@ -0,0 +1,6 @@
+---
+title: Fixes Web IDE button on merge requests when GitLab is installed with relative
+ URL
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml b/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml
new file mode 100644
index 00000000000..0789fc34f27
--- /dev/null
+++ b/changelogs/unreleased/introduce-job-keep-alive-api-endpoint.yml
@@ -0,0 +1,5 @@
+---
+title: Make CI job update entrypoint to work as keep-alive endpoint
+merge_request: 19543
+author:
+type: changed
diff --git a/changelogs/unreleased/issue_44230.yml b/changelogs/unreleased/issue_44230.yml
new file mode 100644
index 00000000000..2c6dba6c0fb
--- /dev/null
+++ b/changelogs/unreleased/issue_44230.yml
@@ -0,0 +1,5 @@
+---
+title: Apply notification settings level of groups to all child objects
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/mk-rake-task-verify-remote-files.yml b/changelogs/unreleased/mk-rake-task-verify-remote-files.yml
new file mode 100644
index 00000000000..772aa11d89b
--- /dev/null
+++ b/changelogs/unreleased/mk-rake-task-verify-remote-files.yml
@@ -0,0 +1,5 @@
+---
+title: Add support for verifying remote uploads, artifacts, and LFS objects in check rake tasks
+merge_request: 19501
+author:
+type: added
diff --git a/changelogs/unreleased/n-plus-one-notification-recipients.yml b/changelogs/unreleased/n-plus-one-notification-recipients.yml
new file mode 100644
index 00000000000..91c31e4c930
--- /dev/null
+++ b/changelogs/unreleased/n-plus-one-notification-recipients.yml
@@ -0,0 +1,5 @@
+---
+title: Fix some sources of excessive query counts when calculating notification recipients
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/pr-importer-io-extra-error-handling.yml b/changelogs/unreleased/pr-importer-io-extra-error-handling.yml
new file mode 100644
index 00000000000..2f7121b2840
--- /dev/null
+++ b/changelogs/unreleased/pr-importer-io-extra-error-handling.yml
@@ -0,0 +1,5 @@
+---
+title: Ensure MR diffs always exist in the PR importer
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47366.yml b/changelogs/unreleased/rails5-fix-47366.yml
new file mode 100644
index 00000000000..7ea03d2b95e
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47366.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix expected `issuable.reload.updated_at` to have changed
+merge_request: 19733
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47370.yml b/changelogs/unreleased/rails5-fix-47370.yml
new file mode 100644
index 00000000000..90c19593b7d
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47370.yml
@@ -0,0 +1,5 @@
+---
+title: Use same gem versions for rails5 as for rails4 where possible
+merge_request: 19498
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47804.yml b/changelogs/unreleased/rails5-fix-47804.yml
new file mode 100644
index 00000000000..3332ed3bbaa
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47804.yml
@@ -0,0 +1,5 @@
+---
+title: Rails5 fix stack level too deep
+merge_request: 19762
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47805.yml b/changelogs/unreleased/rails5-fix-47805.yml
new file mode 100644
index 00000000000..8bd8ad5488c
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47805.yml
@@ -0,0 +1,6 @@
+---
+title: 'Rails5 ActionController::ParameterMissing: param is missing or the value is
+ empty: application_setting'
+merge_request: 19763
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rails5-fix-47835.yml b/changelogs/unreleased/rails5-fix-47835.yml
new file mode 100644
index 00000000000..fe9cbf1a03a
--- /dev/null
+++ b/changelogs/unreleased/rails5-fix-47835.yml
@@ -0,0 +1,6 @@
+---
+title: Rails5 fix no implicit conversion of Hash into String. ActionController::Parameters
+ no longer returns an hash in Rails 5
+merge_request: 19792
+author: Jasper Maes
+type: fixed
diff --git a/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml b/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml
new file mode 100644
index 00000000000..3934381b0cf
--- /dev/null
+++ b/changelogs/unreleased/rd-33733-showing-created-date-instead-of-updated-date-in-project-lists.yml
@@ -0,0 +1,5 @@
+---
+title: Invalidate cache with project details when repository is updated
+merge_request: 19774
+author:
+type: fixed
diff --git a/changelogs/unreleased/rosulk-patch-12.yml b/changelogs/unreleased/rosulk-patch-12.yml
new file mode 100644
index 00000000000..9637c88d1a4
--- /dev/null
+++ b/changelogs/unreleased/rosulk-patch-12.yml
@@ -0,0 +1,5 @@
+---
+title: Flex issue board columns
+merge_request: 19250
+author: Roman Rosluk
+type: changed
diff --git a/changelogs/unreleased/sh-expire-content-cache-after-import.yml b/changelogs/unreleased/sh-expire-content-cache-after-import.yml
new file mode 100644
index 00000000000..8876a487b86
--- /dev/null
+++ b/changelogs/unreleased/sh-expire-content-cache-after-import.yml
@@ -0,0 +1,5 @@
+---
+title: Expire Wiki content cache after importing a repository
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-log-422-responses.yml b/changelogs/unreleased/sh-log-422-responses.yml
new file mode 100644
index 00000000000..c7dfdbb703b
--- /dev/null
+++ b/changelogs/unreleased/sh-log-422-responses.yml
@@ -0,0 +1,6 @@
+---
+title: Log response body to production_json.log when a controller responds with a
+ 422 status
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/sql-buckets.yml b/changelogs/unreleased/sql-buckets.yml
new file mode 100644
index 00000000000..afb13d5cb20
--- /dev/null
+++ b/changelogs/unreleased/sql-buckets.yml
@@ -0,0 +1,5 @@
+---
+title: Adjust SQL and transaction Prometheus buckets
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/tz-diff-blob-image-viewer.yml b/changelogs/unreleased/tz-diff-blob-image-viewer.yml
new file mode 100644
index 00000000000..81d87bc71f5
--- /dev/null
+++ b/changelogs/unreleased/tz-diff-blob-image-viewer.yml
@@ -0,0 +1,5 @@
+---
+title: Web IDE supports now Image + Download Diff Viewing
+merge_request: 18768
+author:
+type: added
diff --git a/changelogs/unreleased/unify-views-search-results.yml b/changelogs/unreleased/unify-views-search-results.yml
new file mode 100644
index 00000000000..81ad3616648
--- /dev/null
+++ b/changelogs/unreleased/unify-views-search-results.yml
@@ -0,0 +1,5 @@
+---
+title: Hide project name if searching against a project
+merge_request: 19595
+author:
+type: changed
diff --git a/changelogs/unreleased/upgrade-gitlab-markup.yml b/changelogs/unreleased/upgrade-gitlab-markup.yml
new file mode 100644
index 00000000000..9b0eaa7068d
--- /dev/null
+++ b/changelogs/unreleased/upgrade-gitlab-markup.yml
@@ -0,0 +1,5 @@
+---
+title: Fix extra blank line at start of rendered reStructuredText code block
+merge_request: 19596
+author:
+type: fixed
diff --git a/changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml b/changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml
new file mode 100644
index 00000000000..4ab9b6dadc0
--- /dev/null
+++ b/changelogs/unreleased/use-tooltip-component-in-mr-widget-author-time-component.yml
@@ -0,0 +1,5 @@
+---
+title: Use Tooltip component in MrWidgetAuthorTime vue comonent
+merge_request: 19635
+author: George Tsiolis
+type: performance
diff --git a/changelogs/unreleased/winh-new-branch-url-encode.yml b/changelogs/unreleased/winh-new-branch-url-encode.yml
new file mode 100644
index 00000000000..f3554d0d4a1
--- /dev/null
+++ b/changelogs/unreleased/winh-new-branch-url-encode.yml
@@ -0,0 +1,5 @@
+---
+title: Fix branch name encoding for dropdown on issue page
+merge_request: 19634
+author:
+type: fixed
diff --git a/config/application.rb b/config/application.rb
index 1b575f1325d..95f6d2c9af1 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -12,6 +12,7 @@ module Gitlab
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')
+ require_dependency Rails.root.join('lib/gitlab/middleware/read_only')
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
@@ -70,6 +71,7 @@ module Gitlab
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
# - Deploy keys (:key)
+ # - File content from Web Editor (:content)
config.filter_parameters += [/token$/, /password/, /secret/]
config.filter_parameters += %i(
certificate
@@ -81,6 +83,7 @@ module Gitlab
sentry_dsn
trace
variables
+ content
)
# Enable escaping HTML in JSON.
@@ -173,7 +176,7 @@ module Gitlab
ENV['GIT_TERMINAL_PROMPT'] = '0'
# Gitlab Read-only middleware support
- config.middleware.insert_after ActionDispatch::Flash, '::Gitlab::Middleware::ReadOnly'
+ config.middleware.insert_after ActionDispatch::Flash, ::Gitlab::Middleware::ReadOnly
config.generators do |g|
g.factory_bot false
diff --git a/config/dependency_decisions.yml b/config/dependency_decisions.yml
index 6616b85129e..21c20cd5e93 100644
--- a/config/dependency_decisions.yml
+++ b/config/dependency_decisions.yml
@@ -534,3 +534,9 @@
:why: https://github.com/squaremo/bitsyntax-js/blob/master/LICENSE-MIT
:versions: []
:when: 2018-02-20 22:20:25.958123000 Z
+- - :approve
+ - "@webassemblyjs/ieee754"
+ - :who: Mike Greiling
+ :why: https://github.com/xtuc/webassemblyjs/blob/master/LICENSE
+ :versions: []
+ :when: 2018-06-08 05:30:56.764116000 Z
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 1849c984351..af1011a1ab1 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,7 +1,7 @@
Rails.application.configure do
# Make sure the middleware is inserted first in middleware chain
- config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestBlockerMiddleware')
- config.middleware.insert_before('ActionDispatch::Static', 'Gitlab::Testing::RequestInspectorMiddleware')
+ config.middleware.insert_before(ActionDispatch::Static, Gitlab::Testing::RequestBlockerMiddleware)
+ config.middleware.insert_before(ActionDispatch::Static, Gitlab::Testing::RequestInspectorMiddleware)
# Settings specified here will take precedence over those in config/application.rb
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 7eb44b8059e..489dc8840e5 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -78,10 +78,15 @@ production: &base
# username_changing_enabled: false # default: true - User can change her username/namespace
## Default theme ID
## 1 - Indigo
- ## 2 - Dark
- ## 3 - Light
- ## 4 - Blue
+ ## 2 - Light Indigo
+ ## 3 - Blue
+ ## 4 - Light Blue
## 5 - Green
+ ## 6 - Light Green
+ ## 7 - Red
+ ## 8 - Light Red
+ ## 9 - Dark
+ ## 10 - Light
# default_theme: 1 # default: 1
## Automatic issue closing
diff --git a/config/initializers/disable_email_interceptor.rb b/config/initializers/disable_email_interceptor.rb
index c76a6b8b19f..e8770c8d460 100644
--- a/config/initializers/disable_email_interceptor.rb
+++ b/config/initializers/disable_email_interceptor.rb
@@ -1,2 +1,5 @@
# Interceptor in lib/disable_email_interceptor.rb
-ActionMailer::Base.register_interceptor(DisableEmailInterceptor) unless Gitlab.config.gitlab.email_enabled
+unless Gitlab.config.gitlab.email_enabled
+ ActionMailer::Base.register_interceptor(DisableEmailInterceptor)
+ ActionMailer::Base.logger = nil
+end
diff --git a/config/initializers/lograge.rb b/config/initializers/lograge.rb
index 114c1cb512f..1cf8a24e98c 100644
--- a/config/initializers/lograge.rb
+++ b/config/initializers/lograge.rb
@@ -27,6 +27,7 @@ unless Sidekiq.server?
gitaly_calls = Gitlab::GitalyClient.get_request_count
payload[:gitaly_calls] = gitaly_calls if gitaly_calls > 0
+ payload[:response] = event.payload[:response] if event.payload[:response]
payload
end
diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
index e9326653cbe..acbdf8de5a6 100644
--- a/config/initializers/mime_types.rb
+++ b/config/initializers/mime_types.rb
@@ -15,3 +15,5 @@ Mime::Type.register "video/ogg", :ogv
Mime::Type.unregister :json
Mime::Type.register 'application/json', :json, [LfsRequest::CONTENT_TYPE, 'application/json']
+
+Mime::Type.register 'image/x-icon', :ico
diff --git a/config/initializers/mini_magick.rb b/config/initializers/mini_magick.rb
new file mode 100644
index 00000000000..db0e7bbaaa3
--- /dev/null
+++ b/config/initializers/mini_magick.rb
@@ -0,0 +1,3 @@
+MiniMagick.configure do |config|
+ config.cli = :graphicsmagick
+end
diff --git a/config/initializers/postgresql_opclasses_support.rb b/config/initializers/postgresql_opclasses_support.rb
index 03bda44a630..7b8afc78817 100644
--- a/config/initializers/postgresql_opclasses_support.rb
+++ b/config/initializers/postgresql_opclasses_support.rb
@@ -41,7 +41,12 @@ module ActiveRecord
# Abstract representation of an index definition on a table. Instances of
# this type are typically created and returned by methods in database
# adapters. e.g. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter#indexes
- class IndexDefinition < Struct.new(:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :opclasses) #:nodoc:
+ attrs = [:table, :name, :unique, :columns, :lengths, :orders, :where, :type, :using, :opclasses]
+
+ # In Rails 5 the second last attribute is newly `:comment`
+ attrs.insert(-2, :comment) if Gitlab.rails5?
+
+ class IndexDefinition < Struct.new(*attrs) #:nodoc:
end
end
end
diff --git a/config/initializers/rack_attack_global.rb b/config/initializers/rack_attack_global.rb
index a90516eee7d..45963831c41 100644
--- a/config/initializers/rack_attack_global.rb
+++ b/config/initializers/rack_attack_global.rb
@@ -26,7 +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.should_be_skipped? &&
req.ip
end
@@ -59,6 +59,10 @@ class Rack::Attack
path =~ %r{^/api/v\d+/internal/}
end
+ def should_be_skipped?
+ api_internal_request?
+ end
+
def web_request?
!api_request?
end
diff --git a/config/locales/carrierwave.en.yml b/config/locales/carrierwave.en.yml
new file mode 100644
index 00000000000..12619226460
--- /dev/null
+++ b/config/locales/carrierwave.en.yml
@@ -0,0 +1,14 @@
+en:
+ errors:
+ messages:
+ carrierwave_processing_error: failed to be processed
+ carrierwave_integrity_error: is not of an allowed file type
+ carrierwave_download_error: could not be downloaded
+ extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
+ extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
+ content_type_whitelist_error: "You are not allowed to upload %{content_type} files"
+ content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
+ rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
+ mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
+ min_size_error: "File size should be greater than %{min_size}"
+ max_size_error: "File size should be less than %{max_size}"
diff --git a/config/prometheus/additional_metrics.yml b/config/prometheus/additional_metrics.yml
index 13732384953..c994bad7865 100644
--- a/config/prometheus/additional_metrics.yml
+++ b/config/prometheus/additional_metrics.yml
@@ -29,14 +29,14 @@
label: Pod average
unit: ms
- title: "HTTP Error Rate"
- y_label: "HTTP 500 Errors / Sec"
+ y_label: "HTTP Errors"
required_metrics:
- nginx_upstream_responses_total
weight: 1
queries:
- - query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m]))'
- label: HTTP Errors
- unit: "errors / sec"
+ - query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100'
+ label: 5xx Errors
+ unit: "%"
- group: Response metrics (HA Proxy)
priority: 10
metrics:
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 3cca1210e39..ff27ceb50dc 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -102,6 +102,7 @@ namespace :admin do
get :preview_sign_in
delete :logo
delete :header_logos
+ delete :favicon
end
end
diff --git a/config/routes/uploads.rb b/config/routes/uploads.rb
index 6370645bcb9..6becadd57ae 100644
--- a/config/routes/uploads.rb
+++ b/config/routes/uploads.rb
@@ -17,7 +17,7 @@ scope path: :uploads do
# Appearance
get "-/system/:model/:mounted_as/:id/:filename",
to: "uploads#show",
- constraints: { model: /appearance/, mounted_as: /logo|header_logo/, filename: /.+/ }
+ constraints: { model: /appearance/, mounted_as: /logo|header_logo|favicon/, filename: /.+/ }
# Project markdown uploads
get ":namespace_id/:project_id/:secret/:filename",
diff --git a/config/routes/wiki.rb b/config/routes/wiki.rb
index c2da84ff6f2..cd3828b743c 100644
--- a/config/routes/wiki.rb
+++ b/config/routes/wiki.rb
@@ -6,7 +6,7 @@ scope(controller: :wikis) do
post '/', to: 'wikis#create'
end
- scope(path: 'wikis/*id', as: :wiki, format: false) do
+ scope(path: 'wikis/*id', as: :wiki, format: false, defaults: { format: :html }) do
get :edit
get :history
post :preview_markdown
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
index cc10da2bd88..220a0191160 100644
--- a/config/unicorn.rb.example
+++ b/config/unicorn.rb.example
@@ -81,6 +81,17 @@ GC.respond_to?(:copy_on_write_friendly=) and
# fast LAN.
check_client_connection false
+before_exec do |server|
+ # The following is necessary to ensure stale Prometheus metrics don't
+ # accumulate over time. It needs to be done in this hook as opposed to
+ # inside an init script to ensure metrics files aren't deleted after new
+ # unicorn workers start after a SIGUSR2 is received.
+ if ENV['prometheus_multiproc_dir']
+ old_metrics = Dir[File.join(ENV['prometheus_multiproc_dir'], '*.db')]
+ FileUtils.rm_rf(old_metrics)
+ end
+end
+
before_fork do |server, worker|
# the following is highly recommended for Rails + "preload_app true"
# as there's no need for the master process to hold a connection
diff --git a/config/webpack.config.js b/config/webpack.config.js
index d6ab32972fb..b1e378f6c27 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -1,11 +1,10 @@
-const fs = require('fs');
const path = require('path');
const glob = require('glob');
const webpack = require('webpack');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const StatsWriterPlugin = require('webpack-stats-plugin').StatsWriterPlugin;
-const CopyWebpackPlugin = require('copy-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
+const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const ROOT_PATH = path.resolve(__dirname, '..');
@@ -168,15 +167,7 @@ module.exports = {
name: '[name].[hash:8].[ext]',
},
},
- {
- test: /monaco-editor\/\w+\/vs\/loader\.js$/,
- use: [
- { loader: 'exports-loader', options: 'l.global' },
- { loader: 'imports-loader', options: 'l=>{},this=>l,AMDLoader=>this,module=>undefined' },
- ],
- },
],
- noParse: [/monaco-editor\/\w+\/vs\//],
},
optimization: {
@@ -226,6 +217,9 @@ module.exports = {
// enable vue-loader to use existing loader rules for other module types
new VueLoaderPlugin(),
+ // automatically configure monaco editor web workers
+ new MonacoWebpackPlugin(),
+
// prevent pikaday from including moment.js
new webpack.IgnorePlugin(/moment/, /pikaday/),
@@ -235,29 +229,6 @@ module.exports = {
jQuery: 'jquery',
}),
- // copy pre-compiled vendor libraries verbatim
- new CopyWebpackPlugin([
- {
- from: path.join(
- ROOT_PATH,
- `node_modules/monaco-editor/${IS_PRODUCTION ? 'min' : 'dev'}/vs`
- ),
- to: 'monaco-editor/vs',
- transform: function(content, path) {
- if (/\.js$/.test(path) && !/worker/i.test(path) && !/typescript/i.test(path)) {
- return (
- '(function(){\n' +
- 'var define = this.define, require = this.require;\n' +
- 'window.define = define; window.require = require;\n' +
- content +
- '\n}.call(window.__monaco_context__ || (window.__monaco_context__ = {})));'
- );
- }
- return content;
- },
- },
- ]),
-
// compression can require a lot of compute time and is disabled in CI
IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
index 213c8bca639..51e69879c79 100644
--- a/db/fixtures/development/04_project.rb
+++ b/db/fixtures/development/04_project.rb
@@ -67,6 +67,10 @@ Sidekiq::Testing.inline! do
skip_disk_validation: true
}
+ if i % 2 == 0
+ params[:storage_version] = Project::LATEST_STORAGE_VERSION
+ end
+
project = Projects::CreateService.new(User.first, params).execute
# Seed-Fu runs this entire fixture in a transaction, so the `after_commit`
# hook won't run until after the fixture is loaded. That is too late
diff --git a/db/fixtures/development/08_settings.rb b/db/fixtures/development/08_settings.rb
new file mode 100644
index 00000000000..141465c06cf
--- /dev/null
+++ b/db/fixtures/development/08_settings.rb
@@ -0,0 +1,7 @@
+# We want to enable hashed storage for every new project in development
+# Details https://gitlab.com/gitlab-org/gitlab-ce/issues/46241
+Gitlab::Seeder.quiet do
+ ApplicationSetting.create_from_defaults unless ApplicationSetting.current_without_cache
+ ApplicationSetting.current_without_cache.update!(hashed_storage_enabled: true)
+ print '.'
+end
diff --git a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
index a96ea7d9db4..dc16d5c5169 100644
--- a/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
+++ b/db/migrate/20161124141322_migrate_process_commit_worker_jobs.rb
@@ -12,7 +12,9 @@ class MigrateProcessCommitWorkerJobs < ActiveRecord::Migration
end
def repository_storage_path
- Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages[repository_storage].legacy_disk_path
+ end
end
def repository_path
diff --git a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
index 8986cd8cb4b..133435523e1 100644
--- a/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
+++ b/db/migrate/20161226122833_remove_dot_git_from_usernames.rb
@@ -64,7 +64,9 @@ class RemoveDotGitFromUsernames < ActiveRecord::Migration
# we rename suffix instead of removing it
path = path.sub(/\.git\z/, '_git')
- check_routes(path.dup, 0, path)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ check_routes(path.dup, 0, path)
+ end
end
def check_routes(base, counter, path)
diff --git a/db/migrate/20170925184228_add_favicon_to_appearances.rb b/db/migrate/20170925184228_add_favicon_to_appearances.rb
new file mode 100644
index 00000000000..65083733afb
--- /dev/null
+++ b/db/migrate/20170925184228_add_favicon_to_appearances.rb
@@ -0,0 +1,7 @@
+class AddFaviconToAppearances < ActiveRecord::Migration
+ DOWNTIME = false
+
+ def change
+ add_column :appearances, :favicon, :string
+ end
+end
diff --git a/db/migrate/20180523042841_rename_merge_requests_allow_maintainer_to_push.rb b/db/migrate/20180523042841_rename_merge_requests_allow_maintainer_to_push.rb
index 975bdfe70f4..41bc7b71694 100644
--- a/db/migrate/20180523042841_rename_merge_requests_allow_maintainer_to_push.rb
+++ b/db/migrate/20180523042841_rename_merge_requests_allow_maintainer_to_push.rb
@@ -6,10 +6,12 @@ class RenameMergeRequestsAllowMaintainerToPush < ActiveRecord::Migration
disable_ddl_transaction!
def up
- rename_column_concurrently :merge_requests, :allow_maintainer_to_push, :allow_collaboration
+ # NOOP
end
def down
- cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
+ if column_exists?(:merge_requests, :allow_collaboration)
+ cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
+ end
end
end
diff --git a/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb
new file mode 100644
index 00000000000..6f50d428965
--- /dev/null
+++ b/db/migrate/20180601213245_add_deploy_strategy_to_project_auto_devops.rb
@@ -0,0 +1,19 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddDeployStrategyToProjectAutoDevops < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :project_auto_devops, :deploy_strategy, :integer, default: 0, allow_null: false
+ end
+
+ def down
+ remove_column :project_auto_devops, :deploy_strategy
+ end
+end
diff --git a/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
new file mode 100644
index 00000000000..dcbbef9bd4a
--- /dev/null
+++ b/db/migrate/20180608110058_rename_merge_requests_allow_collaboration.rb
@@ -0,0 +1,21 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class RenameMergeRequestsAllowCollaboration < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ if column_exists?(:merge_requests, :allow_collaboration)
+ rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
+ end
+ end
+
+ def down
+ # NOOP
+ end
+end
diff --git a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
index b9ce4600675..7301bcf2c6c 100644
--- a/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
+++ b/db/post_migrate/20180523125103_cleanup_merge_requests_allow_maintainer_to_push_rename.rb
@@ -6,10 +6,12 @@ class CleanupMergeRequestsAllowMaintainerToPushRename < ActiveRecord::Migration
disable_ddl_transaction!
def up
- cleanup_concurrent_column_rename :merge_requests, :allow_maintainer_to_push, :allow_collaboration
+ # NOOP
end
def down
- rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
+ if column_exists?(:merge_requests, :allow_collaboration)
+ rename_column_concurrently :merge_requests, :allow_collaboration, :allow_maintainer_to_push
+ end
end
end
diff --git a/db/post_migrate/20180608201435_cleanup_merge_requests_allow_collaboration_rename.rb b/db/post_migrate/20180608201435_cleanup_merge_requests_allow_collaboration_rename.rb
new file mode 100644
index 00000000000..3f3edb8ea3d
--- /dev/null
+++ b/db/post_migrate/20180608201435_cleanup_merge_requests_allow_collaboration_rename.rb
@@ -0,0 +1,20 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class CleanupMergeRequestsAllowCollaborationRename < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ if column_exists?(:merge_requests, :allow_collaboration)
+ cleanup_concurrent_column_rename :merge_requests, :allow_collaboration, :allow_maintainer_to_push
+ end
+ end
+
+ def down
+ # NOOP
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f6fb1c92f8d..d05c6afbb9f 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: 20180603190921) do
+ActiveRecord::Schema.define(version: 20180608201435) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -38,6 +38,7 @@ ActiveRecord::Schema.define(version: 20180603190921) do
t.integer "cached_markdown_version"
t.text "new_project_guidelines"
t.text "new_project_guidelines_html"
+ t.string "favicon"
end
create_table "application_setting_terms", force: :cascade do |t|
@@ -1230,8 +1231,8 @@ ActiveRecord::Schema.define(version: 20180603190921) do
t.boolean "discussion_locked"
t.integer "latest_merge_request_diff_id"
t.string "rebase_commit_sha"
- t.boolean "allow_collaboration"
t.boolean "squash", default: false, null: false
+ t.boolean "allow_maintainer_to_push"
end
add_index "merge_requests", ["assignee_id"], name: "index_merge_requests_on_assignee_id", using: :btree
@@ -1493,6 +1494,7 @@ ActiveRecord::Schema.define(version: 20180603190921) do
t.datetime_with_timezone "updated_at", null: false
t.boolean "enabled"
t.string "domain"
+ t.integer "deploy_strategy", default: 0, null: false
end
add_index "project_auto_devops", ["project_id"], name: "index_project_auto_devops_on_project_id", unique: true, using: :btree
diff --git a/doc/administration/index.md b/doc/administration/index.md
index df935095e61..0e65f9a9963 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -49,6 +49,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
#### Customizing GitLab's appearance
- [Header logo](../customization/branded_page_and_email_header.md): Change the logo on all pages and email headers.
+- [Favicon](../customization/favicon.md): Change the default favicon to your own logo.
- [Branded login page](../customization/branded_login_page.md): Customize the login page with your own logo, title, and description.
- [Welcome message](../customization/welcome_message.md): Add a custom welcome message to the sign-in page.
- ["New Project" page](../customization/new_project_page.md): Customize the text to be displayed on the page that opens whenever your users create a new project.
diff --git a/doc/administration/integration/koding.md b/doc/administration/integration/koding.md
index 67f9f01efb8..6c1ec3028cc 100644
--- a/doc/administration/integration/koding.md
+++ b/doc/administration/integration/koding.md
@@ -100,14 +100,14 @@ As it's pointed out before, you will need public access to this machine that
you've installed Koding and GitLab on. Better to use a domain but a static IP
is also fine.
-For IP based installation you can use [xip.io](https://xip.io) service which is
+For IP based installation you can use [nip.io](https://nip.io) service which is
free and provides DNS resolution to IP based requests like following;
- - 127.0.0.1.xip.io -> resolves to 127.0.0.1
- - foo.bar.baz.127.0.0.1.xip.io -> resolves to 127.0.0.1
+ - 127.0.0.1.nip.io -> resolves to 127.0.0.1
+ - foo.bar.baz.127.0.0.1.nip.io -> resolves to 127.0.0.1
- and so on...
-As Koding needs subdomains for team names; `foo.127.0.0.1.xip.io` requests for
+As Koding needs subdomains for team names; `foo.127.0.0.1.nip.io` requests for
a running koding instance on `127.0.0.1` server will be handled as `foo` team
requests.
@@ -127,8 +127,8 @@ your Koding installation. Team called `gitlab` has integration on Koding out
of the box, so if you didn't change anything your team on Koding should be
`gitlab`.
-So, if your Koding is running on `http://1.2.3.4.xip.io:8090` your URL needs
-to be `http://gitlab.1.2.3.4.xip.io:8090`. You need to provide the same host
+So, if your Koding is running on `http://1.2.3.4.nip.io:8090` your URL needs
+to be `http://gitlab.1.2.3.4.nip.io:8090`. You need to provide the same host
with your Koding installation here.
@@ -192,7 +192,7 @@ cd koding
docker-compose run \
--service-ports backend \
/opt/koding/scripts/bootstrap-container build \
- --host=**YOUR_IP**.xip.io \
+ --host=**YOUR_IP**.nip.io \
--gitlabHost=**GITLAB_IP** \
--gitlabPort=**GITLAB_PORT** \
--gitlabToken=**SECRET_TOKEN** \
@@ -224,7 +224,7 @@ cd koding
docker-compose run \
--service-ports backend \
/opt/koding/scripts/bootstrap-container build \
- --host=**YOUR_IP**.xip.io \
+ --host=**YOUR_IP**.nip.io \
```
#### Enable Single Sign On
@@ -233,7 +233,7 @@ Once you restarted your Koding and logged in with your username and password
you need to activate oauth authentication for your user. To do that
- Navigate to Dashboard on Koding from;
- `http://gitlab.**YOUR_IP**.xip.io:8090/Home/my-account`
+ `http://gitlab.**YOUR_IP**.nip.io:8090/Home/my-account`
- Scroll down to Integrations section
- Click on toggle to turn On integration in GitLab integration section
diff --git a/doc/administration/raketasks/check.md b/doc/administration/raketasks/check.md
index 7d34d35e7d1..2649bf61d74 100644
--- a/doc/administration/raketasks/check.md
+++ b/doc/administration/raketasks/check.md
@@ -78,9 +78,10 @@ Example output:
## Uploaded Files Integrity
-Various types of file can be uploaded to a GitLab installation by users.
-Checksums are generated and stored in the database upon upload, and integrity
-checks using those checksums can be run. These checks also detect missing files.
+Various types of files can be uploaded to a GitLab installation by users.
+These integrity checks can detect missing files. Additionally, for locally
+stored files, checksums are generated and stored in the database upon upload,
+and these checks will verify them against current files.
Currently, integrity checks are supported for the following types of file:
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index cfd601b8866..7ad38abe4f5 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -17,13 +17,21 @@ This task will schedule all your existing projects and attachments associated wi
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:migrate_to_hashed
+sudo gitlab-rake gitlab:storage:migrate_to_hashed
```
**Source Installation**
```bash
-rake gitlab:storage:migrate_to_hashed
+sudo -u git -H bundle exec rake gitlab:storage:migrate_to_hashed RAILS_ENV=production
+```
+
+They both also accept a range as environment variable:
+
+```bash
+# to migrate any non migrated project from ID 20 to 50.
+export ID_FROM=20
+export ID_TO=50
```
You can monitor the progress in the _Admin > Monitoring > Background jobs_ screen.
@@ -44,13 +52,13 @@ To have a simple summary of projects using **Legacy** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:legacy_projects
+sudo gitlab-rake gitlab:storage:legacy_projects
```
**Source Installation**
```bash
-rake gitlab:storage:legacy_projects
+sudo -u git -H bundle exec rake gitlab:storage:legacy_projects RAILS_ENV=production
```
------
@@ -60,13 +68,13 @@ To list projects using **Legacy** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:list_legacy_projects
+sudo gitlab-rake gitlab:storage:list_legacy_projects
```
**Source Installation**
```bash
-rake gitlab:storage:list_legacy_projects
+sudo -u git -H bundle exec rake gitlab:storage:list_legacy_projects RAILS_ENV=production
```
@@ -77,13 +85,13 @@ To have a simple summary of projects using **Hashed** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:hashed_projects
+sudo gitlab-rake gitlab:storage:hashed_projects
```
**Source Installation**
```bash
-rake gitlab:storage:hashed_projects
+sudo -u git -H bundle exec rake gitlab:storage:hashed_projects RAILS_ENV=production
```
------
@@ -93,14 +101,13 @@ To list projects using **Hashed** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:list_hashed_projects
+sudo gitlab-rake gitlab:storage:list_hashed_projects
```
**Source Installation**
```bash
-rake gitlab:storage:list_hashed_projects
-
+sudo -u git -H bundle exec rake gitlab:storage:list_hashed_projects RAILS_ENV=production
```
## List attachments on Legacy storage
@@ -110,13 +117,13 @@ To have a simple summary of project attachments using **Legacy** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:legacy_attachments
+sudo gitlab-rake gitlab:storage:legacy_attachments
```
**Source Installation**
```bash
-rake gitlab:storage:legacy_attachments
+sudo -u git -H bundle exec rake gitlab:storage:legacy_attachments RAILS_ENV=production
```
------
@@ -126,13 +133,13 @@ To list project attachments using **Legacy** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:list_legacy_attachments
+sudo gitlab-rake gitlab:storage:list_legacy_attachments
```
**Source Installation**
```bash
-rake gitlab:storage:list_legacy_attachments
+sudo -u git -H bundle exec rake gitlab:storage:list_legacy_attachments RAILS_ENV=production
```
## List attachments on Hashed storage
@@ -142,13 +149,13 @@ To have a simple summary of project attachments using **Hashed** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:hashed_attachments
+sudo gitlab-rake gitlab:storage:hashed_attachments
```
**Source Installation**
```bash
-rake gitlab:storage:hashed_attachments
+sudo -u git -H bundle exec rake gitlab:storage:hashed_attachments RAILS_ENV=production
```
------
@@ -158,13 +165,13 @@ To list project attachments using **Hashed** storage:
**Omnibus Installation**
```bash
-gitlab-rake gitlab:storage:list_hashed_attachments
+sudo gitlab-rake gitlab:storage:list_hashed_attachments
```
**Source Installation**
```bash
-rake gitlab:storage:list_hashed_attachments
+sudo -u git -H bundle exec rake gitlab:storage:list_hashed_attachments RAILS_ENV=production
```
[storage-types]: ../repository_storage_types.md
diff --git a/doc/api/commits.md b/doc/api/commits.md
index d1584cf64de..d07b9d5614a 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -16,6 +16,7 @@ GET /projects/:id/repository/commits
| `until` | string | no | Only commits before or on this date will be returned in ISO 8601 format YYYY-MM-DDTHH:MM:SSZ |
| `path` | string | no | The file path |
| `all` | boolean | no | Retrieve every commit from the repository |
+| `with_stats` | boolean | no | Stats about each commit will be added to the response |
```bash
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index 9f06e20f803..da74045b702 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -70,18 +70,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
@@ -190,18 +190,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
@@ -297,18 +297,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 2,
"target_project_id": 3,
@@ -548,14 +548,16 @@ Parameters:
"username": "jarrett",
"id": 5,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/b95567800f828948baf5f4160ebb2473?s=40&d=identicon"
+ "avatar_url": "http://www.gravatar.com/avatar/b95567800f828948baf5f4160ebb2473?s=40&d=identicon",
+ "web_url" : "https://gitlab.example.com/jarrett"
},
"assignee": {
"name": "Administrator",
"username": "root",
"id": 1,
"state": "active",
- "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40&d=identicon"
+ "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=40&d=identicon",
+ "web_url" : "https://gitlab.example.com/root"
},
"source_project_id": 4,
"target_project_id": 4,
@@ -669,18 +671,18 @@ POST /projects/:id/merge_requests
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 3,
"target_project_id": 4,
@@ -761,18 +763,18 @@ Must include at least one non-required attribute from above.
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 3,
"target_project_id": 4,
@@ -870,18 +872,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 4,
"target_project_id": 4,
@@ -949,18 +951,18 @@ Parameters:
"author": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"assignee": {
"id": 1,
"username": "admin",
- "email": "admin@example.com",
"name": "Administrator",
"state": "active",
- "created_at": "2012-04-29T08:46:00Z"
+ "avatar_url": null,
+ "web_url" : "https://gitlab.example.com/admin"
},
"source_project_id": 4,
"target_project_id": 4,
diff --git a/doc/api/snippets.md b/doc/api/snippets.md
index 42b760c107d..7892866cd8e 100644
--- a/doc/api/snippets.md
+++ b/doc/api/snippets.md
@@ -49,6 +49,7 @@ Example response:
"title": "test",
"file_name": "add.rb",
"description": "Ruby test snippet",
+ "visibility": "private",
"author": {
"id": 1,
"username": "john_smith",
@@ -99,6 +100,7 @@ Example response:
"title": "This is a snippet",
"file_name": "test.txt",
"description": "Hello World snippet",
+ "visibility": "internal",
"author": {
"id": 1,
"username": "john_smith",
@@ -150,6 +152,7 @@ Example response:
"title": "test",
"file_name": "add.rb",
"description": "description of snippet",
+ "visibility": "internal",
"author": {
"id": 1,
"username": "john_smith",
@@ -238,7 +241,8 @@ Example response:
"raw_url": "http://localhost:3000/snippets/48/raw",
"title": "Minus similique nesciunt vel fugiat qui ullam sunt.",
"updated_at": "2016-11-25T16:53:34.479Z",
- "web_url": "http://localhost:3000/snippets/48"
+ "web_url": "http://localhost:3000/snippets/48",
+ "visibility": "public"
}
]
```
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 36fd8affa5b..8ea2e0a81dc 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -114,7 +114,7 @@ Let's now see how that information is exposed within GitLab.
## Viewing the current status of an environment
-The environment list under your project's **Pipelines ➔ Environments**, is
+The environment list under your project's **Operations > Environments**, is
where you can find information of the last deployment status of an environment.
Here's how the Environments page looks so far.
@@ -167,7 +167,7 @@ that works.
You can't control everything, so sometimes things go wrong. When that unfortunate
time comes GitLab has you covered. Simply by clicking the **Rollback** button
that can be found in the deployments page
-(**Pipelines ➔ Environments ➔ `environment name`**) you can relaunch the
+(**Operations > Environments > `environment name`**) you can relaunch the
job with the commit associated with it.
>**Note:**
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index de60cd27cd1..aa31e172641 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -19,7 +19,9 @@ There's also a collection of repositories with [example projects](https://gitlab
- [How to test and deploy Laravel/PHP applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md)
- **Ruby**: [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
- **Python**: [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md)
-- **Java**: [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/)
+- **Java**:
+ - [Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD](deploy_spring_boot_to_cloud_foundry/index.md)
+ - [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/)
- **Scala**: [Test a Scala application](test-scala-application.md)
- **Clojure**: [Test a Clojure application](test-clojure-application.md)
- **Elixir**:
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png
new file mode 100644
index 00000000000..5b5d91ec07a
--- /dev/null
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/cloud_foundry_secret_variables.png
Binary files differ
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png
new file mode 100644
index 00000000000..f3761632556
--- /dev/null
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/img/create_from_template.png
Binary files differ
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
new file mode 100644
index 00000000000..b88761be56b
--- /dev/null
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
@@ -0,0 +1,142 @@
+---
+author: Dylan Griffith
+author_gitlab: DylanGriffith
+level: intermediary
+article_type: tutorial
+date: 2018-06-07
+description: "Continuous Deployment of a Spring Boot application to Cloud Foundry with GitLab CI/CD"
+---
+
+# Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD
+
+## Introduction
+
+In this article, we'll demonstrate how to deploy a [Spring
+Boot](https://projects.spring.io/spring-boot/) application to [Cloud
+Foundry (CF)](https://www.cloudfoundry.org/) with GitLab CI/CD using the [Continuous
+Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/#continuous-deployment)
+method.
+
+All the code for this project can be found in this [GitLab
+repo](https://gitlab.com/gitlab-examples/spring-gitlab-cf-deploy-demo).
+
+In case you're interested in deploying Spring Boot applications to Kubernetes
+using GitLab CI/CD, read through the blog post [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/).
+
+## Requirements
+
+_We assume you are familiar with Java, GitLab, Cloud Foundry, and GitLab CI/CD._
+
+To follow along with this tutorial you will need the following:
+
+- An account on [Pivotal Web Services (PWS)](https://run.pivotal.io/) or any
+ other Cloud Foundry instance
+- An account on GitLab
+
+NOTE: **Note:**
+You will need to replace the `api.run.pivotal.io` URL in the all below
+commands with the [API
+URL](https://docs.cloudfoundry.org/running/cf-api-endpoint.html) of your CF
+instance if you're not deploying to PWS.
+
+## Create your project
+
+To create your Spring Boot application you can use the Spring template in
+GitLab when creating a new project:
+
+![New Project From Template](img/create_from_template.png)
+
+## Configure the deployment to Cloud Foundry
+
+To deploy to Cloud Foundry we need to add a `manifest.yml` file. This
+is the configuration for the CF CLI we will use to deploy the application. We
+will create this in the root directory of our project with the following
+content:
+
+```yaml
+---
+applications:
+- name: gitlab-hello-world
+ random-route: true
+ memory: 1G
+ path: target/demo-0.0.1-SNAPSHOT.jar
+```
+
+## Configure GitLab CI/CD to deploy your application
+
+Now we need to add the the GitLab CI/CD configuration file
+([`.gitlab-ci.yml`](../../yaml/README.md)) to our
+project's root. This is how GitLab figures out what commands need to be run whenever
+code is pushed to our repository. We will add the following `.gitlab-ci.yml`
+file to the root directory of the repository, GitLab will detect it
+automatically and run the steps defined once we push our code:
+
+```yaml
+image: java:8
+
+stages:
+ - build
+ - deploy
+
+build:
+ stage: build
+ script: ./mvnw package
+ artifacts:
+ paths:
+ - target/demo-0.0.1-SNAPSHOT.jar
+
+production:
+ stage: deploy
+ script:
+ - curl --location "https://cli.run.pivotal.io/stable?release=linux64-binary&source=github" | tar zx
+ - ./cf login -u $CF_USERNAME -p $CF_PASSWORD -a api.run.pivotal.io
+ - ./cf push
+ only:
+ - master
+```
+
+We've used the `java:8` [docker
+image](../../docker/using_docker_images.md) to build
+our application as it provides the up-to-date Java 8 JDK on [Docker
+Hub](https://hub.docker.com/). We've also added the [`only`
+clause](../../yaml/README.md#only-and-except-simplified)
+to ensure our deployments only happen when we push to the master branch.
+
+Now, since the steps defined in `.gitlab-ci.yml` require credentials to login
+to CF, you'll need to add your CF credentials as [environment
+variables](../../variables/README.md#predefined-variables-environment-variables)
+on GitLab CI/CD. To set the environment variables, navigate to your project's
+**Settings > CI/CD** and expand **Secret Variables**. Name the variables
+`CF_USERNAME` and `CF_PASSWORD` and set them to the correct values.
+
+![Secret Variable Settings in GitLab](img/cloud_foundry_secret_variables.png)
+
+Once set up, GitLab CI/CD will deploy your app to CF at every push to your
+repository's deafult branch. To see the build logs or watch your builds running
+live, navigate to **CI/CD > Pipelines**.
+
+CAUTION: **Caution:**
+It is considered best practice for security to create a separate deploy
+user for your application and add its credentials to GitLab instead of using
+a developer's credentials.
+
+To start a manual deployment in GitLab go to **CI/CD > Pipelines** then click
+on **Run Pipeline**. Once the app is finished deploying it will display the URL
+of your application in the logs for the `production` job like:
+
+```shell
+requested state: started
+instances: 1/1
+usage: 1G x 1 instances
+urls: gitlab-hello-world-undissembling-hotchpot.cfapps.io
+last uploaded: Mon Nov 6 10:02:25 UTC 2017
+stack: cflinuxfs2
+buildpack: client-certificate-mapper=1.2.0_RELEASE container-security-provider=1.8.0_RELEASE java-buildpack=v4.5-offline-https://github.com/cloudfoundry/java-buildpack.git#ffeefb9 java-main java-opts jvmkill-agent=1.10.0_RELEASE open-jdk-like-jre=1.8.0_1...
+
+ state since cpu memory disk details
+#0 running 2017-11-06 09:03:22 PM 120.4% 291.9M of 1G 137.6M of 1G
+```
+
+You can then visit your deployed application (for this example,
+https://gitlab-hello-world-undissembling-hotchpot.cfapps.io/) and you should
+see the "Spring is here!" message.
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 1b24bcdbf6f..a3da6515a19 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -64,6 +64,7 @@ future GitLab releases.**
| **CI_JOB_NAME** | 9.0 | 0.5 | The name of the job as defined in `.gitlab-ci.yml` |
| **CI_JOB_STAGE** | 9.0 | 0.5 | The name of the stage as defined in `.gitlab-ci.yml` |
| **CI_JOB_TOKEN** | 9.0 | 1.2 | Token used for authenticating with the GitLab Container Registry |
+| **CI_JOB_URL** | 11.0 | 0.5 | Job details URL |
| **CI_REPOSITORY_URL** | 9.0 | all | The URL to clone the Git repository |
| **CI_RUNNER_DESCRIPTION** | 8.10 | 0.5 | The description of the runner as saved in GitLab |
| **CI_RUNNER_ID** | 8.10 | 0.5 | The unique id of runner being used |
@@ -81,6 +82,7 @@ future GitLab releases.**
| **CI_PROJECT_NAMESPACE** | 8.10 | 0.5 | The project namespace (username or groupname) that is currently being built |
| **CI_PROJECT_PATH** | 8.10 | 0.5 | The namespace with project name |
| **CI_PROJECT_PATH_SLUG** | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
+| **CI_PIPELINE_URL** | 11.0 | 0.5 | Pipeline details URL |
| **CI_PROJECT_URL** | 8.10 | 0.5 | The HTTP address to access project |
| **CI_PROJECT_VISIBILITY** | 10.3 | all | The project visibility (internal, private, public) |
| **CI_REGISTRY** | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index f946536701e..8e13ceb9257 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -799,16 +799,6 @@ cache:
- binaries/
```
-If you use **Windows PowerShell** to run your shell scripts you need to replace
-`$` with `$env:`:
-
-```yaml
-cache:
- key: "$env:CI_COMMIT_REF_SLUG"
- paths:
- - binaries/
-```
-
### `cache:untracked`
Set `untracked: true` to cache all files that are untracked in your Git
diff --git a/doc/customization/favicon.md b/doc/customization/favicon.md
new file mode 100644
index 00000000000..45a18159b5e
--- /dev/null
+++ b/doc/customization/favicon.md
@@ -0,0 +1,16 @@
+# Changing the favicon
+
+> [Introduced][ce-14497] in GitLab 11.0.
+
+[ce-14497]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14497
+
+Navigate to the **Admin** area and go to the **Appearance** page.
+
+Upload the custom favicon (**Favicon**) in the section **Favicon**.
+
+![appearance](favicon/appearance.png)
+
+After saving the page, the new favicon will be shown in the browser. The main
+favicon as well as the CI status icons will show the custom icon:
+
+![custom_favicon](favicon/custom_favicon.png)
diff --git a/doc/customization/favicon/appearance.png b/doc/customization/favicon/appearance.png
new file mode 100644
index 00000000000..6c41a05fc1f
--- /dev/null
+++ b/doc/customization/favicon/appearance.png
Binary files differ
diff --git a/doc/customization/favicon/custom_favicon.png b/doc/customization/favicon/custom_favicon.png
new file mode 100644
index 00000000000..fa1b8827a36
--- /dev/null
+++ b/doc/customization/favicon/custom_favicon.png
Binary files differ
diff --git a/doc/development/README.md b/doc/development/README.md
index 92d9829192e..5d6fed5bc72 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -32,7 +32,7 @@ description: 'Learn how to contribute to GitLab.'
- [GitLab utilities](utilities.md)
- [API styleguide](api_styleguide.md) Use this styleguide if you are
contributing to the API.
-- [GrapQL API styleguide](api_graphql_styleguide.md) Use this
+- [GraphQL API styleguide](api_graphql_styleguide.md) Use this
styleguide if you are contribution to the [GraphQL API](../api/graphql/index.md)
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
- [Working with Gitaly](gitaly.md)
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index a9fa5ae834f..9e0c81b3d60 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -45,6 +45,8 @@ the `author` field. GitLab team members **should not**.
a changelog entry regardless of these guidelines if the contributor wants one.
Example: "Fixed a typo on the search results page. (Jane Smith)"
- Performance improvements **should** have a changelog entry.
+- Any change that introduces a database migration **must** have a
+ changelog entry.
## Writing good changelog entries
diff --git a/doc/development/fe_guide/style_guide_scss.md b/doc/development/fe_guide/style_guide_scss.md
index 655d94793dd..48eb6d0a7d6 100644
--- a/doc/development/fe_guide/style_guide_scss.md
+++ b/doc/development/fe_guide/style_guide_scss.md
@@ -216,12 +216,12 @@ If you want a line or set of lines to be ignored by the linter, you can use
`// scss-lint:disable RuleName` ([more info][disabling-linters]):
```scss
-// This lint rule is disabled because the class name comes from a gem.
-// scss-lint:disable SelectorFormat
-.ui_indigo {
- background-color: #333;
+// This lint rule is disabled because it is supported only in Chrome/Safari
+// scss-lint:disable PropertySpelling
+body {
+ text-decoration-skip: ink;
}
-// scss-lint:enable SelectorFormat
+// scss-lint:enable PropertySpelling
```
Make sure a comment is added on the line above the `disable` rule, otherwise the
diff --git a/doc/development/i18n/externalization.md b/doc/development/i18n/externalization.md
index 0edcb23c7c5..4ba9958e2c6 100644
--- a/doc/development/i18n/externalization.md
+++ b/doc/development/i18n/externalization.md
@@ -233,7 +233,7 @@ This makes use of [`Intl.DateTimeFormat`].
Please never split a sentence as that would assume the sentence grammar and
structure is the same in all languages.
-For instance, the following
+For instance, the following:
```js
{{ s__("mrWidget|Set by") }}
@@ -247,6 +247,27 @@ should be externalized as follows:
{{ sprintf(s__("mrWidget|Set by %{author} to be merged automatically when the pipeline succeeds"), { author: author.name }) }}
```
+#### Avoid splitting sentences when adding links
+
+This also applies when using links in between translated sentences, otherwise these texts are not translatable in certain languages.
+
+Instead of:
+
+```haml
+- zones_link = link_to(s_('ClusterIntegration|zones'), 'https://cloud.google.com/compute/docs/regions-zones/regions-zones', target: '_blank', rel: 'noopener noreferrer')
+= s_('ClusterIntegration|Learn more about %{zones_link}').html_safe % { zones_link: zones_link }
+```
+
+Set the link starting and ending HTML fragments as variables like so:
+
+```haml
+- zones_link_url = 'https://cloud.google.com/compute/docs/regions-zones/regions-zones'
+- zones_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: zones_link_url }
+= s_('ClusterIntegration|Learn more about %{zones_link_start}zones%{zones_link_end}').html_safe % { zones_link_start: zones_link_start, zones_link_end: '</a>'.html_safe }
+```
+
+The reasoning behind this is that in some languages words change depending on context. For example in Japanese は is added to the subject of a sentence and を to the object. This is impossible to translate correctly if we extract individual words from the sentence.
+
When in doubt, try to follow the best practices described in this [Mozilla
Developer documentation][mdn].
diff --git a/doc/development/new_fe_guide/development/testing.md b/doc/development/new_fe_guide/development/testing.md
index c359bd83ed1..e1e13474b75 100644
--- a/doc/development/new_fe_guide/development/testing.md
+++ b/doc/development/new_fe_guide/development/testing.md
@@ -1,3 +1,135 @@
-# Testing
+# Overview of Frontend Testing
-> TODO: Add content
+## Types of tests in our codebase
+
+* **RSpec**
+ * **[Ruby unit tests](#ruby-unit-tests-spec-rb)** for models, controllers, helpers, etc. (`/spec/**/*.rb`)
+ * **[Full feature tests](#full-feature-tests-spec-features-rb)** (`/spec/features/**/*.rb`)
+* **[Karma](#karma-tests-spec-javascripts-js)** (`/spec/javascripts/**/*.js`)
+* ~~Spinach~~ — These have been removed from our codebase in May 2018. (`/features/`)
+
+## RSpec: Ruby unit tests `/spec/**/*.rb`
+
+These tests are meant to unit test the ruby models, controllers and helpers.
+
+### When do we write/update these tests?
+
+Whenever we create or modify any Ruby models, controllers or helpers we add/update corresponding tests.
+
+---
+
+## RSpec: Full feature tests `/spec/features/**/*.rb`
+
+Full feature tests will load a full app environment and allow us to test things like rendering DOM, interacting with links and buttons, testing the outcome of those interactions through multiple pages if necessary. These are also called end-to-end tests but should not be confused with QA end-to-end tests (`package-and-qa` manual pipeline job).
+
+### When do we write/update these tests?
+
+When we add a new feature, we write at least two tests covering the success and the failure scenarios.
+
+### Relevant notes
+
+A `:js` flag is added to the test to make sure the full environment is loaded.
+
+```
+scenario 'successfully', :js do
+ sign_in(create(:admin))
+end
+```
+
+The steps of each test are written using capybara methods ([documentation](http://www.rubydoc.info/gems/capybara/2.15.1)).
+
+Bear in mind <abbr title="XMLHttpRequest">XHR</abbr> calls might require you to use `wait_for_requests` in between steps, like so:
+
+```rspec
+find('.form-control').native.send_keys(:enter)
+
+wait_for_requests
+
+expect(page).not_to have_selector('.card')
+```
+
+---
+
+## Karma tests `/spec/javascripts/**/*.js`
+
+These are the more frontend-focused, at the moment. They're **faster** than `rspec` and make for very quick testing of frontend components.
+
+### When do we write/update these tests?
+
+When we add/update a method/action/mutation to Vue or Vuex, we write karma tests to ensure the logic we wrote doesn't break. We should, however, refrain from writing tests that double-test Vue's internal features.
+
+### Relevant notes
+
+Karma tests are run against a virtual DOM.
+
+To populate the DOM, we can use fixtures to fake the generation of HTML instead of having Rails do that.
+
+Be sure to check the [best practices for karma tests](../../testing_guide/frontend_testing.html#best-practices).
+
+### Vue and Vuex
+
+Test as much as possible without double-testing Vue's internal features, as mentioned above.
+
+Make sure to test computedProperties, mutations, actions. Run the action and test that the proper mutations are committed.
+
+Also check these [notes on testing Vue components](../../fe_guide/vue.html#testing-vue-components).
+
+#### Vuex Helper: `testAction`
+
+We have a helper available to make testing actions easier, as per [official documentation](https://vuex.vuejs.org/en/testing.html):
+
+```
+testAction(
+ actions.actionName, // action
+ { }, // params to be passed to action
+ state, // state
+ [
+ { type: types.MUTATION},
+ { type: types.MUTATION_1, payload: {}},
+ ], // mutations committed
+ [
+ { type: 'actionName', payload: {}},
+ { type: 'actionName1', payload: {}},
+ ] // actions dispatched
+ done,
+);
+```
+
+Check an example in [spec/javascripts/ide/stores/actions_spec.jsspec/javascripts/ide/stores/actions_spec.js](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/javascripts/ide/stores/actions_spec.js).
+
+#### Vue Helper: `mountComponent`
+
+To make mounting a Vue component easier and more readable, we have a few helpers available in `spec/helpers/vue_mount_component_helper`.
+
+* `createComponentWithStore`
+* `mountComponentWithStore`
+
+Examples of usage:
+
+```
+beforeEach(() => {
+ vm = createComponentWithStore(Component, store);
+
+ vm.$store.state.currentBranchId = 'master';
+
+ vm.$mount();
+},
+```
+
+```
+beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ el: '#dummy-element',
+ store,
+ props: { badge },
+ });
+},
+```
+
+Don't forget to clean up:
+
+```
+afterEach(() => {
+ vm.$destroy();
+});
+```
diff --git a/doc/gitlab-basics/command-line-commands.md b/doc/gitlab-basics/command-line-commands.md
index c9766040234..4666511d747 100644
--- a/doc/gitlab-basics/command-line-commands.md
+++ b/doc/gitlab-basics/command-line-commands.md
@@ -7,17 +7,19 @@ In Git, when you copy a project you say you "clone" it. To work on a git project
When you are on your Dashboard, click on the project that you'd like to clone.
To work in the project, you can copy a link to the Git repository through a SSH
or a HTTPS protocol. SSH is easier to use after it's been
-[setup](create-your-ssh-keys.md). While you are at the **Project** tab, select
-HTTPS or SSH from the dropdown menu and copy the link using the 'Copy to clipboard'
+[set up](create-your-ssh-keys.md). While you are at the **Project** tab, select
+HTTPS or SSH from the dropdown menu and copy the link using the _Copy URL to clipboard_
button (you'll have to paste it on your shell in the next step).
![Copy the HTTPS or SSH](img/project_clone_url.png)
## On the command line
+This section has examples of some basic shell commands that you might find useful. For more information, search the web for _bash commands_.
+
### Clone your project
-Go to your computer's shell and type the following command:
+Go to your computer's shell and type the following command with your SSH or HTTPS URL:
```
git clone PASTE HTTPS OR SSH HERE
@@ -25,33 +27,45 @@ git clone PASTE HTTPS OR SSH HERE
A clone of the project will be created in your computer.
->**Note:** If you clone your project via an URL that contains special characters, make sure that they are URL-encoded.
+>**Note:** If you clone your project via a URL that contains special characters, make sure that characters are URL-encoded.
-### Go into a project, directory or file to work in it
+### Go into a project directory to work in it
```
-cd NAME-OF-PROJECT-OR-FILE
+cd NAME-OF-PROJECT
```
-### Go back one directory or file
+### Go back one directory
```
-cd ../
+cd ..
```
-### View what’s in the directory that you are in
+### List what’s in the current directory
```
ls
```
-### Create a directory
+### List what’s in the current directory that starts with `a`
+
+```
+ls a*
+```
+
+### List what’s in the current directory that ends with `.md`
+
+```
+ls *.md
+```
+
+### Create a new directory
```
mkdir NAME-OF-YOUR-DIRECTORY
```
-### Create a README.md or file in directory
+### Create a README.md file in the current directory
```
touch README.md
@@ -62,6 +76,12 @@ nano README.md
#### Press: enter
```
+### Show the contents of the README.md file
+
+```
+cat README.md
+```
+
### Remove a file
```
@@ -74,12 +94,18 @@ rm NAME-OF-FILE
rm -r NAME-OF-DIRECTORY
```
-### View history in the command line
+### View command history
```
history
```
+### Execute command 123 from history
+
+```
+!123
+```
+
### Carry out commands for which the account you are using lacks authority
You will be asked for an administrator’s password.
@@ -88,8 +114,14 @@ You will be asked for an administrator’s password.
sudo
```
-### Tell where you are
+### Show which directory I am in
```
pwd
```
+
+### Clear the shell window
+
+```
+clear
+```
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index 42cd8bb3e48..0d9994c9925 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -17,112 +17,197 @@ Depending on your operating system, you will need to use a shell of your prefere
Git is usually preinstalled on Mac and Linux.
Type the following command and then press enter:
-```
+
+```bash
git --version
```
-You should receive a message that will tell you which Git version you have on your computer. If you don’t receive a "Git version" message, it means that you need to [download Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
+You should receive a message that tells you which Git version you have on your computer. If you don’t receive a "Git version" message, it means that you need to [download Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git).
If Git doesn't automatically download, there's an option on the website to [download manually](https://git-scm.com/downloads). Then follow the steps on the installation window.
-After you are finished installing, open a new shell and type "git --version" again to verify that it was correctly installed.
+After you are finished installing Git, open a new shell and type `git --version` again to verify that it was correctly installed.
## Add your Git username and set your email
-It is important to configure your Git username and email address as every Git commit will use this information to identify you as the author.
+It is important to configure your Git username and email address, since every Git commit will use this information to identify you as the author.
On your shell, type the following command to add your username:
-```
+
+```bash
git config --global user.name "YOUR_USERNAME"
```
Then verify that you have the correct username:
-```
+
+```bash
git config --global user.name
```
To set your email address, type the following command:
-```
+
+```bash
git config --global user.email "your_email_address@example.com"
```
To verify that you entered your email correctly, type:
-```
+
+```bash
git config --global user.email
```
-You'll need to do this only once as you are using the `--global` option. It tells Git to always use this information for anything you do on that system. If you want to override this with a different username or email address for specific projects, you can run the command without the `--global` option when you’re in that project.
+You'll need to do this only once, since you are using the `--global` option. It tells Git to always use this information for anything you do on that system. If you want to override this with a different username or email address for specific projects, you can run the command without the `--global` option when you’re in that project.
## Check your information
-To view the information that you entered, type:
-```
+To view the information that you entered, along with other global options, type:
+
+```bash
git config --global --list
```
+
## Basic Git commands
### Go to the master branch to pull the latest changes from there
-```
+```bash
git checkout master
```
### Download the latest changes in the project
-This is for you to work on an up-to-date copy (it is important to do every time you work on a project), while you setup tracking branches.
+
+This is for you to work on an up-to-date copy (it is important to do this every time you start working on a project), while you set up tracking branches. You pull from remote repositories to get all the changes made by users since the last time you cloned or pulled the project. Later, you can push your local commits to the remote repositories.
+
+```bash
+git pull REMOTE NAME-OF-BRANCH
```
-git pull REMOTE NAME-OF-BRANCH -u
+
+When you first clone a repository, REMOTE is typically "origin". This is where the repository came from, and it indicates the SSH or HTTPS URL of the repository on the remote server. NAME-OF-BRANCH is usually "master", but it may be any existing branch.
+
+### View your remote repositories
+
+To view your remote repositories, type:
+
+```bash
+git remote -v
```
-(REMOTE: origin) (NAME-OF-BRANCH: could be "master" or an existing branch)
### Create a branch
-Spaces won't be recognized, so you will need to use a hyphen or underscore.
-```
+
+To create a branch, type the following (spaces won't be recognized in the branch name, so you will need to use a hyphen or underscore):
+
+```bash
git checkout -b NAME-OF-BRANCH
```
-### Work on a branch that has already been created
-```
+### Work on an existing branch
+
+To switch to an existing branch, so you can work on it:
+
+```bash
git checkout NAME-OF-BRANCH
```
### View the changes you've made
-It's important to be aware of what's happening and what's the status of your changes.
-```
+
+It's important to be aware of what's happening and the status of your changes. When you add, change, or delete files/folders, Git knows about it. To check the status of your changes:
+
+```bash
git status
```
-### Add changes to commit
-You'll see your changes in red when you type "git status".
+### View differences
+
+To view the differences between your local, unstaged changes and the repository versions that you cloned or pulled, type:
+
+```bash
+git diff
+```
+
+### Add and commit local changes
+
+You'll see your local changes in red when you type `git status`. These changes may be new, modified, or deleted files/folders. Use `git add` to stage a local file/folder for committing. Then use `git commit` to commit the staged files:
+
+```bash
+git add FILE OR FOLDER
+git commit -m "COMMENT TO DESCRIBE THE INTENTION OF THE COMMIT"
```
-git add CHANGES IN RED
-git commit -m "DESCRIBE THE INTENTION OF THE COMMIT"
+
+### Add all changes to commit
+
+To add and commit all local changes in one command:
+
+```bash
+git add .
+git commit -m "COMMENT TO DESCRIBE THE INTENTION OF THE COMMIT"
```
+NOTE: **Note:**
+The `.` character typically means _all_ in Git.
+
### Send changes to gitlab.com
-```
+
+To push all local commits to the remote repository:
+
+```bash
git push REMOTE NAME-OF-BRANCH
```
-### Delete all changes in the Git repository, but leave unstaged things
+For example, to push your local commits to the _master_ branch of the _origin_ remote:
+
+```bash
+git push origin master
```
+
+### Delete all changes in the Git repository
+
+To delete all local changes in the repository that have not been added to the staging area, and leave unstaged files/folders, type:
+
+```bash
git checkout .
```
-### Delete all changes in the Git repository, including untracked files
-```
+### Delete all untracked changes in the Git repository
+
+```bash
git clean -f
```
+### Unstage all changes that have been added to the staging area
+
+To undo the most recent add, but not committed, files/folders:
+
+```bash
+git reset .
+```
+
+### Undo most recent commit
+
+To undo the most recent commit, type:
+
+```bash
+git reset HEAD~1
+```
+
+This leaves the files and folders unstaged in your local repository.
+
+CAUTION: **Warning:**
+A Git commit is mostly irreversible, particularly if you already pushed it to the remote repository. Although you can undo a commit, the best option is to avoid the situation altogether.
+
### Merge created branch with master branch
+
You need to be in the created branch.
-```
+
+```bash
git checkout NAME-OF-BRANCH
git merge master
```
### Merge master branch with created branch
+
You need to be in the master branch.
-```
+
+```bash
git checkout master
git merge NAME-OF-BRANCH
```
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 34268c67140..6cd1fb4c2d7 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -301,9 +301,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-8-stable gitlab
+ sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 11-0-stable gitlab
-**Note:** You can change `10-8-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `11-0-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/kubernetes/gitlab_omnibus.md b/doc/install/kubernetes/gitlab_omnibus.md
index e1d1969651e..852a58a9afc 100644
--- a/doc/install/kubernetes/gitlab_omnibus.md
+++ b/doc/install/kubernetes/gitlab_omnibus.md
@@ -120,7 +120,7 @@ gitlabConfigStorageSize: 1Gi
Ingress routing and SSL are automatically configured within this Chart. An NGINX ingress is provisioned and configured, and will route traffic to any service. SSL certificates are automatically created and configured by [kube-lego](https://github.com/kubernetes/charts/tree/master/stable/kube-lego).
> **Note:**
-Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [xip.io](http://xip.io) and [nip.io](http://nip.io) are unlikely to work.
+Let's Encrypt limits a single TLD to five certificate requests within a single week. This means that common DNS wildcard services like [nip.io](http://nip.io) and [nip.io](http://nip.io) are unlikely to work.
## Installing GitLab using the Helm Chart
> **Note:**
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index 1ced1fb513d..60e1e2b5f8a 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -307,10 +307,10 @@ hostname** and use greater values for the volume sizes. If you don't provide a
password for PostgreSQL, it will be created automatically.
>**Note:**
-The `gitlab.apps.10.2.2.2.xip.io` hostname that is used by default will
+The `gitlab.apps.10.2.2.2.nip.io` hostname that is used by default will
resolve to the host with IP `10.2.2.2` which is the IP our VM uses. It is a
trick to have distinct FQDNs pointing to services that are on our local network.
-Read more on how this works in <http://xip.io>.
+Read more on how this works in <http://nip.io>.
Now that we configured this, let's see how to manage and scale GitLab.
@@ -347,7 +347,7 @@ Navigate back to the **Overview** and hopefully all pods will be up and running.
![GitLab running](img/gitlab-running.png)
Congratulations! You can now navigate to your new shinny GitLab instance by
-visiting <http://gitlab.apps.10.2.2.2.xip.io> where you will be asked to
+visiting <http://gitlab.apps.10.2.2.2.nip.io> where you will be asked to
change the root user password. Login using `root` as username and providing the
password you just set, and start using GitLab!
@@ -521,4 +521,4 @@ PaaS and managing your applications with the ease of containers.
[autoscaling]: https://docs.openshift.org/latest/dev_guide/pod_autoscaling.html "Documentation - Autoscale"
[basic-cli]: https://docs.openshift.org/latest/cli_reference/basic_cli_operations.html "Documentation - Basic CLI operations"
[openshift-docs]: https://docs.openshift.org "OpenShift documentation"
-[scc]: https://docs.openshift.org/latest/admin_guide/manage_scc.html "Documentation - Managing Security Context Constraints" \ No newline at end of file
+[scc]: https://docs.openshift.org/latest/admin_guide/manage_scc.html "Documentation - Managing Security Context Constraints"
diff --git a/doc/topics/autodevops/img/autodevops_domain_variables.png b/doc/topics/autodevops/img/autodevops_domain_variables.png
new file mode 100644
index 00000000000..b6f8864796f
--- /dev/null
+++ b/doc/topics/autodevops/img/autodevops_domain_variables.png
Binary files differ
diff --git a/doc/topics/autodevops/img/autodevops_multiple_clusters.png b/doc/topics/autodevops/img/autodevops_multiple_clusters.png
new file mode 100644
index 00000000000..f4d101ca921
--- /dev/null
+++ b/doc/topics/autodevops/img/autodevops_multiple_clusters.png
Binary files differ
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index fec575f263f..103836e59d0 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -41,6 +41,7 @@ project in an easy and automatic way:
1. [Auto Code Quality](#auto-code-quality)
1. [Auto SAST (Static Application Security Testing)](#auto-sast)
1. [Auto Dependency Scanning](#auto-dependency-scanning)
+1. [Auto License Management](#auto-license-management)
1. [Auto Container Scanning](#auto-container-scanning)
1. [Auto Review Apps](#auto-review-apps)
1. [Auto DAST (Dynamic Application Security Testing)](#auto-dast)
@@ -62,7 +63,7 @@ Auto DevOps provides great defaults for all the stages; you can, however,
For an overview on the creation of Auto DevOps, read the blog post [From 2/3 of the Self-Hosted Git Market, to the Next-Generation CI System, to Auto DevOps](https://about.gitlab.com/2017/06/29/whats-next-for-gitlab-ci/).
-## Prerequisites
+## Requirements
TIP: **Tip:**
For self-hosted installations, the easiest way to make use of Auto DevOps is to
@@ -112,30 +113,31 @@ NOTE: **Note:**
If you do not have Kubernetes or Prometheus installed, then Auto Review Apps,
Auto Deploy, and Auto Monitoring will be silently skipped.
-### Auto DevOps base domain
+## Auto DevOps base domain
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
-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`.
+Review Apps](#auto-review-apps) and [Auto Deploy](#auto-deploy). It can be defined
+in three places:
-A wildcard DNS A record matching the base domain is required, for example,
+- either under the project's CI/CD settings while [enabling Auto DevOps](#enabling-auto-devops)
+- or in instance-wide settings in the **admin area > Settings** under the "Continuous Integration and Delivery" section
+- or at the project or group level as a variable: `AUTO_DEVOPS_DOMAIN` (required if you want to use [multiple clusters](#using-multiple-kubernetes-clusters))
+
+A wildcard DNS A record matching the base domain(s) is required, for example,
given a base domain of `example.com`, you'd need a DNS entry like:
```
*.example.com 3600 A 1.2.3.4
```
-where `example.com` is the domain name under which the deployed apps will be served,
+In this case, `example.com` is the domain name under which the deployed apps will be served,
and `1.2.3.4` is the IP address of your load balancer; generally NGINX
-([see prerequisites](#prerequisites)). How to set up the DNS record is beyond
+([see requirements](#requirements)). How to set up the DNS record is beyond
the scope of this document; you should check with your DNS provider.
-Alternatively you can use free public services like [xip.io](http://xip.io) or
+Alternatively you can use free public services like [nip.io](http://nip.io) or
[nip.io](http://nip.io) which provide automatic wildcard DNS without any
-configuration. Just set the Auto DevOps base domain to `1.2.3.4.xip.io` or
+configuration. Just set the Auto DevOps base domain to `1.2.3.4.nip.io` or
`1.2.3.4.nip.io`.
Once set up, all requests will hit the load balancer, which in turn will route
@@ -146,6 +148,56 @@ If GitLab is installed using the [GitLab Omnibus Helm Chart], there are two
options: provide a static IP, or have one assigned. For more information see the
relevant docs on the [network prerequisites](../../install/kubernetes/gitlab_omnibus.md#networking-prerequisites).
+## Using multiple Kubernetes clusters **[PREMIUM]**
+
+When using Auto DevOps, you may want to deploy different environments to
+different Kubernetes clusters. This is possible due to the 1:1 connection that
+[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters).
+
+In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml)
+(used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know:
+
+- `review/` (every environment starting with `review/`)
+- `staging`
+- `production`
+
+Those environments are tied to jobs that use [Auto Deploy](#auto-deploy), so
+except for the environment scope, they would also need to have a different
+domain they would be deployed to. This is why you need to define a separate
+`AUTO_DEVOPS_DOMAIN` variable for all the above
+[based on the environment](../../ci/variables/README.md#limiting-environment-scopes-of-variables).
+
+The following table is an example of how the three different clusters would
+be configured.
+
+| Cluster name | Cluster environment scope | `AUTO_DEVOPS_DOMAIN` variable value | Variable environment scope | Notes |
+| ------------ | -------------- | ----------------------------- | ------------- | ------ |
+| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. |
+| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). |
+| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production). |
+
+To add a different cluster for each environment:
+
+1. Navigate to your project's **Operations > Kubernetes** and create the Kubernetes clusters
+ with their respective environment scope as described from the table above.
+
+ ![Auto DevOps multiple clusters](img/autodevops_multiple_clusters.png)
+
+1. After the clusters are created, navigate to each one and install Helm Tiller
+ and Ingress.
+1. Make sure you have [configured your DNS](#auto-devops-base-domain) with the
+ specified Auto DevOps domains.
+1. Navigate to your project's **Settings > CI/CD > Variables** and add
+ the `AUTO_DEVOPS_DOMAIN` variables with their respective environment
+ scope.
+
+ ![Auto DevOps domain variables](img/autodevops_domain_variables.png)
+
+Now that all is configured, you can test your setup by creating a merge request
+and verifying that your app is deployed as a review app in the Kubernetes
+cluster with the `review/*` environment scope. Similarly, you can check the
+other environments.
+
## Quick start
If you are using GitLab.com, see our [quick start guide](quick_start_guide.md)
@@ -154,18 +206,18 @@ Google Cloud.
## Enabling Auto DevOps
-If you haven't done already, read the [prerequisites](#prerequisites) to make
+If you haven't done already, read the [requirements](#requirements) to make
full use of Auto DevOps. If this is your fist time, we recommend you follow the
-[quick start guide](#quick-start).
+[quick start guide](quick_start_guide.md).
To enable Auto DevOps to your project:
-1. Check that your project doesn't have a `.gitlab-ci.yml`, and remove it otherwise
-1. Go to your project's **Settings > CI/CD > General pipelines settings** and
- find the Auto DevOps section
+1. Check that your project doesn't have a `.gitlab-ci.yml`, or remove it otherwise
+1. Go to your project's **Settings > CI/CD > Auto DevOps**
1. Select "Enable Auto DevOps"
1. Optionally, but recommended, add in the [base domain](#auto-devops-base-domain)
- that will be used by Kubernetes to deploy your application
+ that will be used by Kubernetes to [deploy your application](#auto-deploy)
+ and choose the [deployment strategy](#deployment-strategy)
1. Hit **Save changes** for the changes to take effect
Once saved, an Auto DevOps pipeline will be triggered on the default branch.
@@ -182,6 +234,24 @@ in **Admin Area > Settings > Continuous Integration and Deployment**. Doing that
all the projects that haven't explicitly set an option will have Auto DevOps
enabled by default.
+### Deployment strategy
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/38542) in GitLab 11.0.
+
+You can change the deployment strategy used by Auto DevOps by going to your
+project's **Settings > CI/CD > Auto DevOps**.
+
+The available options are:
+
+- **Continuous deployment to production** - enables [Auto Deploy](#auto-deploy)
+ by setting the [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
+ [`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
+ to false.
+- **Automatic deployment to staging, manual deployment to production** - sets the
+ [`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
+ [`INCREMENTAL_ROLLOUT_ENABLED`](#incremental-rollout-to-production) variables
+ to true, and the user is responsible for manually deploying to staging and production.
+
## Stages of Auto DevOps
The following sections describe the stages of Auto DevOps. Read them carefully
@@ -230,7 +300,7 @@ In GitLab Starter, differences between the source and
target branches are also
[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality_diff.html).
-### Auto SAST
+### Auto SAST **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 10.3.
@@ -241,9 +311,9 @@ report is created, it's uploaded as an artifact which you can later download and
check out.
In GitLab Ultimate, any security warnings are also
-[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/sast.html).
+[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/sast.html).
-### Auto Dependency Scanning
+### Auto Dependency Scanning **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 10.7.
@@ -254,7 +324,20 @@ report is created, it's uploaded as an artifact which you can later download and
check out.
In GitLab Ultimate, any security warnings are also
-[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/dependency_scanning.html).
+[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/dependency_scanning.html).
+
+### Auto License Management **[ULTIMATE]**
+
+> Introduced in [GitLab Ultimate][ee] 11.0.
+
+License Management uses the
+[License Management Docker image](https://gitlab.com/gitlab-org/security-products/license_management)
+to search the project dependencies for their license. Once the
+report is created, it's uploaded as an artifact which you can later download and
+check out.
+
+In GitLab Ultimate, any licenses are also
+[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/license_management.html).
### Auto Container Scanning
@@ -267,13 +350,13 @@ created, it's uploaded as an artifact which you can later download and
check out.
In GitLab Ultimate, any security warnings are also
-[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html).
+[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/container_scanning.html).
### Auto Review Apps
NOTE: **Note:**
This is an optional step, since many projects do not have a Kubernetes cluster
-available. If the [prerequisites](#prerequisites) are not met, the job will
+available. If the [requirements](#requirements) are not met, the job will
silently be skipped.
CAUTION: **Caution:**
@@ -295,7 +378,7 @@ up in the merge request widget for easy discovery. When the branch is deleted,
for example after the merge request is merged, the Review App will automatically
be deleted.
-### Auto DAST
+### Auto DAST **[ULTIMATE]**
> Introduced in [GitLab Ultimate][ee] 10.4.
@@ -306,9 +389,9 @@ issues. Once the report is created, it's uploaded as an artifact which you can
later download and check out.
In GitLab Ultimate, any security warnings are also
-[shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/dast.html).
+[shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/dast.html).
-### Auto Browser Performance Testing
+### Auto Browser Performance Testing **[PREMIUM]**
> Introduced in [GitLab Premium][ee] 10.4.
@@ -320,13 +403,14 @@ Auto Browser Performance Testing utilizes the [Sitespeed.io container](https://h
/direction
```
-In GitLab Premium, performance differences between the source and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html).
+In GitLab Premium, performance differences between the source
+and target branches are [shown in the merge request widget](https://docs.gitlab.com/ee//user/project/merge_requests/browser_performance_testing.html).
### Auto Deploy
NOTE: **Note:**
This is an optional step, since many projects do not have a Kubernetes cluster
-available. If the [prerequisites](#prerequisites) are not met, the job will
+available. If the [requirements](#requirements) are not met, the job will
silently be skipped.
CAUTION: **Caution:**
@@ -360,10 +444,19 @@ no longer be valid as soon as the deployment job finishes. This means that
Kubernetes can run the application, but in case it should be restarted or
executed somewhere else, it cannot be accessed again.
+> [Introduced][ce-19507] in GitLab 11.0.
+
+For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md###gitlab-deploy-token)
+will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
+can be used for permanent access to the registry.
+
+Note: **Note**
+When the GitLab Deploy Token has been manually revoked, it won't be automatically created.
+
### Auto Monitoring
NOTE: **Note:**
-Check the [prerequisites](#prerequisites) for Auto Monitoring to make this stage
+Check the [requirements](#requirements) for Auto Monitoring to make this stage
work.
Once your application is deployed, Auto Monitoring makes it possible to monitor
@@ -437,7 +530,7 @@ repo or by specifying a project variable:
file in it, Auto DevOps will detect the chart and use it instead of the [default
one](https://gitlab.com/charts/charts.gitlab.io/tree/master/charts/auto-deploy-app).
This can be a great way to control exactly how your application is deployed.
-- **Project variable** - Create a [variable](../../ci/variables/README.md#variables)
+- **Project variable** - Create a [project variable](../../ci/variables/README.md#secret-variables)
`AUTO_DEVOPS_CHART` with the URL of a custom chart to use.
### Customizing `.gitlab-ci.yml`
@@ -493,21 +586,23 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. |
| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-variables-environment-variables). Set it to use a custom database name. |
| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` |
+| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.|
+| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).|
| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments). |
| `INCREMENTAL_ROLLOUT_ENABLED`| From GitLab 10.8, this variable can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment. |
| `TEST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `test` job. If the variable is present, the job will not be created. |
-| `CODE_QUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `code_quality` job. If the variable is present, the job will not be created. |
+| `CODEQUALITY_DISABLED` | From GitLab 11.0, this variable can be used to disable the `codequality` job. If the variable is present, the job will not be created. |
| `SAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast` job. If the variable is present, the job will not be created. |
| `DEPENDENCY_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dependency_scanning` job. If the variable is present, the job will not be created. |
-| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `container_scanning` job. If the variable is present, the job will not be created. |
+| `CONTAINER_SCANNING_DISABLED` | From GitLab 11.0, this variable can be used to disable the `sast:container` job. If the variable is present, the job will not be created. |
| `REVIEW_DISABLED` | From GitLab 11.0, this variable can be used to disable the `review` and the manual `review:stop` job. If the variable is present, these jobs will not be created. |
| `DAST_DISABLED` | From GitLab 11.0, this variable can be used to disable the `dast` job. If the variable is present, the job will not be created. |
| `PERFORMANCE_DISABLED` | From GitLab 11.0, this variable can be used to disable the `performance` job. If the variable is present, the job will not be created. |
TIP: **Tip:**
Set up the replica variables using a
-[project variable](../../ci/variables/README.md#variables)
+[project variable](../../ci/variables/README.md#secret-variables)
and scale your application by just redeploying it!
CAUTION: **Caution:**
@@ -575,6 +670,9 @@ service:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ci-yml/merge_requests/160)
in GitLab 10.8.
+TIP: **Tip:**
+You can also set this inside your [project's settings](#deployment-strategy).
+
The normal behavior of Auto DevOps is to use Continuous Deployment, pushing
automatically to the `production` environment every time a new pipeline is run
on the default branch. However, there are cases where you might want to use a
@@ -582,7 +680,7 @@ staging environment and deploy to production manually. For this scenario, the
`STAGING_ENABLED` environment variable was introduced.
If `STAGING_ENABLED` is defined in your project (e.g., set `STAGING_ENABLED` to
-`1` as a variable), then the application will be automatically deployed
+`1` as a secret variable), then the application will be automatically deployed
to a `staging` environment, and a `production_manual` job will be created for
you when you're ready to manually deploy to production.
@@ -595,7 +693,7 @@ A [canary environment](https://docs.gitlab.com/ee/user/project/canary_deployment
before any changes are deployed to production.
If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
-`1` as a variable) then two manual jobs will be created:
+`1` as a secret variable) then two manual jobs will be created:
- `canary` which will deploy the application to the canary environment
- `production_manual` which is to be used by you when you're ready to manually
@@ -605,13 +703,16 @@ If `CANARY_ENABLED` is defined in your project (e.g., set `CANARY_ENABLED` to
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/5415) in GitLab 10.8.
+TIP: **Tip:**
+You can also set this inside your [project's settings](#deployment-strategy).
+
When you have a new version of your app to deploy in production, you may want
to use an incremental rollout to replace just a few pods with the latest code.
This will allow you to first check how the app is behaving, and later manually
increasing the rollout up to 100%.
If `INCREMENTAL_ROLLOUT_ENABLED` is defined in your project (e.g., set
-`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a variable), then instead of the
+`INCREMENTAL_ROLLOUT_ENABLED` to `1` as a secret variable), then instead of the
standard `production` job, 4 different
[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph)
will be created:
@@ -741,3 +842,4 @@ curl --data "value=true" --header "PRIVATE-TOKEN: personal_access_token" https:/
[Auto DevOps template]: https://gitlab.com/gitlab-org/gitlab-ci-yml/blob/master/Auto-DevOps.gitlab-ci.yml
[GitLab Omnibus Helm Chart]: ../../install/kubernetes/gitlab_omnibus.md
[ee]: https://about.gitlab.com/products/
+[ce-19507]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19507
diff --git a/doc/university/README.md b/doc/university/README.md
index aa68c841f92..595fc480887 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -15,11 +15,11 @@ Would you like to contribute to GitLab University? Then please take a look at ou
The curriculum is composed of GitLab videos, screencasts, presentations, projects and external GitLab content hosted on other services and has been organized into the following sections.
-1. [GitLab Beginner](#beginner)
-1. [GitLab Intermediate](#intermediate)
-1. [GitLab Advanced](#advanced)
-1. [External Articles](#external)
-1. [Resources for GitLab Team Members](#team)
+1. [GitLab Beginner](#1-gitlab-beginner)
+1. [GitLab Intermediate](#2-gitlab-intermediate)
+1. [GitLab Advanced](#3-gitlab-advanced)
+1. [External Articles](#4-external-articles)
+1. [Resources for GitLab Team Members](#5-resources-for-gitlab-team-members)
---
@@ -126,7 +126,7 @@ The curriculum is composed of GitLab videos, screencasts, presentations, project
1. [IBM: Continuous Delivery vs Continuous Deployment - Video](https://www.youtube.com/watch?v=igwFj8PPSnw)
1. [Amazon: Transition to Continuous Delivery - Video](https://www.youtube.com/watch?v=esEFaY0FDKc)
2. [TechBeacon: Doing continuous delivery? Focus first on reducing release cycle times](https://techbeacon.com/doing-continuous-delivery-focus-first-reducing-release-cycle-times)
-1. See **[Integrations](#integrations)** for integrations with other CI services.
+1. See **[Integrations](#39-integrations)** for integrations with other CI services.
#### 2.4. Workflow
diff --git a/doc/university/glossary/README.md b/doc/university/glossary/README.md
index 945d6a578b0..89516dba60b 100644
--- a/doc/university/glossary/README.md
+++ b/doc/university/glossary/README.md
@@ -557,10 +557,6 @@ Software that is hosted centrally and accessed on-demand (i.e. whenever you want
This term is often used by people when they mean "Version Control."
-#### SCLAU
-
-Abbreviation for SQO Count [Large And Up](https://about.gitlab.com/handbook/sales/#market-segmentation). This is the number of opportunities in large and strategic organizations passed from marketing to sales.
-
### Scrum
An Agile [framework](https://www.scrum.org/Resources/What-is-Scrum) designed to typically help complete complex software projects. It's made up of several parts: product requirements backlog, sprint planning, sprint (development), sprint review, and retrospec (analyzing the sprint). The goal is to end up with potentially shippable products.
diff --git a/doc/update/10.8-to-11.0.md b/doc/update/10.8-to-11.0.md
new file mode 100644
index 00000000000..78a47ab939f
--- /dev/null
+++ b/doc/update/10.8-to-11.0.md
@@ -0,0 +1,362 @@
+---
+comments: false
+---
+
+# From 10.8 to 11.0
+
+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 11.0 and higher only support Ruby 2.4.x and dropped support for Ruby 2.3.x. Be
+sure to upgrade your interpreter if necessary.
+
+You can check which version you are running with `ruby -v`.
+
+Download Ruby and compile it:
+
+```bash
+mkdir /tmp/ruby && cd /tmp/ruby
+curl --remote-name --progress https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.4.tar.gz
+echo 'ec82b0d53bd0adad9b19e6b45e44d54e9ec3f10c ruby-2.4.4.tar.gz' | shasum -c - && tar xzf ruby-2.4.4.tar.gz
+cd ruby-2.4.4
+
+./configure --disable-install-rdoc
+make
+sudo make install
+```
+
+Install Bundler:
+
+```bash
+sudo gem install bundler --no-ri --no-rdoc
+```
+
+### 4. Update Node
+
+GitLab utilizes [webpack](http://webpack.js.org) to compile frontend assets.
+This requires a minimum version of node v6.0.0.
+
+You can check which version you are running with `node -v`. If you are running
+a version older than `v6.0.0` you will need to update to a newer version. You
+can find instructions to install from community maintained packages or compile
+from source at the nodejs.org website.
+
+<https://nodejs.org/en/download/>
+
+GitLab also requires the use of yarn `>= v1.2.0` to manage JavaScript
+dependencies.
+
+```bash
+curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
+echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
+sudo apt-get update
+sudo apt-get install yarn
+```
+
+More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
+
+### 5. Update Go
+
+NOTE: GitLab 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 --prune
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+sudo -u git -H git checkout -- locale
+```
+
+For GitLab Community Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-0-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H git checkout 11-0-stable-ee
+```
+
+### 7. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
+sudo -u git -H bin/compile
+```
+
+### 8. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. GitLab-Workhorse uses
+[GNU Make](https://www.gnu.org/software/make/).
+If you are not using Linux you may have to run `gmake` instead of
+`make` below.
+
+```bash
+cd /home/git/gitlab-workhorse
+
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
+```
+
+### 9. Update Gitaly
+
+#### New Gitaly configuration options required
+
+In order to function Gitaly needs some additional configuration information. Below we assume you installed Gitaly in `/home/git/gitaly` and GitLab Shell in `/home/git/gitlab-shell`.
+
+```shell
+echo '
+[gitaly-ruby]
+dir = "/home/git/gitaly/ruby"
+
+[gitlab-shell]
+dir = "/home/git/gitlab-shell"
+' | sudo -u git tee -a /home/git/gitaly/config.toml
+```
+
+#### Check Gitaly configuration
+
+Due to a bug in the `rake gitlab:gitaly:install` script your Gitaly
+configuration file may contain syntax errors. The block name
+`[[storages]]`, which may occur more than once in your `config.toml`
+file, should be `[[storage]]` instead.
+
+```shell
+sudo -u git -H sed -i.pre-10.1 's/\[\[storages\]\]/[[storage]]/' /home/git/gitaly/config.toml
+```
+
+#### Compile Gitaly
+
+```shell
+cd /home/git/gitaly
+sudo -u git -H git fetch --all --tags --prune
+sudo -u git -H git checkout v$(</home/git/gitlab/GITALY_SERVER_VERSION)
+sudo -u git -H make
+```
+
+### 10. Update 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-8-stable:config/gitlab.yml.example origin/11-0-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-8-stable:lib/support/nginx/gitlab-ssl origin/11-0-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/10-8-stable:lib/support/nginx/gitlab origin/11-0-stable:lib/support/nginx/gitlab
+```
+
+If you are using Strict-Transport-Security in your installation to continue using it you must enable it in your Nginx
+configuration as GitLab application no longer handles setting it.
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-0-stable/lib/support/init.d/gitlab.default.example#L38
+
+#### SMTP configuration
+
+If you're installing from source and use SMTP to deliver mail, you will need to add the following line
+to config/initializers/smtp_settings.rb:
+
+```ruby
+ActionMailer::Base.delivery_method = :smtp
+```
+
+See [smtp_settings.rb.sample] as an example.
+
+[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-0-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-8-stable:lib/support/init.d/gitlab.default.example origin/11-0-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.8)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 10.7 to 10.8](10.7-to-10.8.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
+
+[yaml]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-0-stable/config/gitlab.yml.example
+[gl-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/11-0-stable/lib/support/init.d/gitlab.default.example
diff --git a/doc/user/admin_area/settings/img/enforce_terms.png b/doc/user/admin_area/settings/img/enforce_terms.png
index e5f0a2683b5..3d93e1cc891 100644
--- a/doc/user/admin_area/settings/img/enforce_terms.png
+++ b/doc/user/admin_area/settings/img/enforce_terms.png
Binary files differ
diff --git a/doc/user/admin_area/settings/img/sign_up_terms.png b/doc/user/admin_area/settings/img/sign_up_terms.png
new file mode 100644
index 00000000000..4cac9d426c9
--- /dev/null
+++ b/doc/user/admin_area/settings/img/sign_up_terms.png
Binary files differ
diff --git a/doc/user/admin_area/settings/terms.md b/doc/user/admin_area/settings/terms.md
index 8e1fb982aba..aa817c9a209 100644
--- a/doc/user/admin_area/settings/terms.md
+++ b/doc/user/admin_area/settings/terms.md
@@ -20,6 +20,19 @@ When an admin enables this feature, they will automattically be
directed to the page to accept the terms themselves. After they
accept, they will be directed back to the settings page.
+## New registrations
+
+When this feature is enabled, a checkbox will be available in the
+sign-up form.
+
+![Sign up form](img/sign_up_terms.png)
+
+This checkbox will be required during sign up.
+
+Users can review the terms entered in the admin panel before
+accepting. The page will be opened in a new window so they can
+continue their registration afterwards.
+
## Accepting terms
When this feature was enabled, the users that have not accepted the
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index 0c1cd113686..d054561d5f3 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -60,6 +60,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
| Setting | GitLab.com | Default |
| ----------- | ----------------- | ------------- |
| Artifacts maximum size | 1G | 100M |
+| Artifacts [expiry time](../../ci/yaml/README.md#artifacts-expire_in) | kept forever | deleted after 30 days unless otherwise specified |
## Repository size limit
diff --git a/doc/user/index.md b/doc/user/index.md
index 640edab6ea4..edf50019c2f 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -23,7 +23,7 @@ documentation.
GitLab is a fully integrated software development platform that enables you
and your team to work cohesively, faster, transparently, and effectively,
since the discussion of a new idea until taking that idea to production all
-all the way through, from within the same platform.
+the way through, from within the same platform.
Please check this page for an overview on [GitLab's features](https://about.gitlab.com/features/).
@@ -161,13 +161,13 @@ such as Trello, JIRA, etc.
## Webhooks
-Configure [webhooks](project/integrations/webhooks.html) to listen for
+Configure [webhooks](project/integrations/webhooks.md) to listen for
specific events like pushes, issues or merge requests. GitLab will send a
POST request with data to the webhook URL.
## API
-Automate GitLab via [API](../api/README.html).
+Automate GitLab via [API](../api/README.md).
## Git and GitLab
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index 650d60f1585..8e87c896a72 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -3,13 +3,15 @@
## GitLab Flavored Markdown (GFM)
> **Note:**
-Not all of the GitLab-specific extensions to Markdown that are described in
-this document currently work on our documentation website.
+> Not all of the GitLab-specific extensions to Markdown that are described in
+> this document currently work on our documentation website.
>
-For the best result, we encourage you to check this document out as rendered
+> For the best result, we encourage you to check this document out as rendered
by GitLab: [markdown.md]
-_GitLab uses the [Redcarpet Ruby library][redcarpet] for Markdown processing._
+_GitLab uses (as of 11.1) the [CommonMark Ruby Library][commonmarker] for Markdown processing of all new issues, merge requests, comments, and other Markdown content in the GitLab system. Previous content and Markdown files `.md` in the repositories are still processed using the [Redcarpet Ruby library][redcarpet]._
+
+_Where there are significant differences, we will try to call them out in this document._
GitLab uses "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
@@ -21,7 +23,7 @@ You can use GFM in the following areas:
- milestones
- snippets (the snippet must be named with a `.md` extension)
- wiki pages
-- markdown documents inside the repository
+- markdown documents inside the repository (currently only rendered by Redcarpet)
You can also use other rich text files in GitLab. You might have to install a
dependency to do so. Please see the [github-markup gem readme](https://github.com/gitlabhq/markup#markups) for more information.
@@ -34,7 +36,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#newline
GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p).
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
-Line-breaks, or softreturns, are rendered if you end a line with two or more spaces:
+Line-breaks, or soft returns, are rendered if you end a line with two or more spaces:
[//]: # (Do *NOT* remove the two ending whitespaces in the following line.)
[//]: # (They are needed for the Markdown text to render correctly.)
@@ -197,7 +199,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-
With inline diffs tags you can display {+ additions +} or [- deletions -].
-The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
+The wrapping tags can be either curly braces or square brackets: [+ additions +] or {- deletions -}.
Examples:
@@ -228,7 +230,7 @@ https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
- 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.
+ 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 one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
@@ -238,7 +240,7 @@ Sometimes you want to :monkey: around a bit and add some :star2: to your :speech
You can use it to point out a :bug: or warn about :speak_no_evil: patches. And if someone improves your really :snail: code, send them some :birthday:. People will :heart: you for that.
-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.
+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 one of the supported codes.
Consult the [Emoji Cheat Sheet](https://www.emojicopy.com) for a list of all supported emoji codes. :thumbsup:
@@ -394,17 +396,17 @@ Color written inside backticks will be followed by a color "chip".
Examples:
- `#F00`
- `#F00A`
- `#FF0000`
- `#FF0000AA`
- `RGB(0,255,0)`
- `RGB(0%,100%,0%)`
- `RGBA(0,255,0,0.7)`
- `HSL(540,70%,50%)`
+ `#F00`
+ `#F00A`
+ `#FF0000`
+ `#FF0000AA`
+ `RGB(0,255,0)`
+ `RGB(0%,100%,0%)`
+ `RGBA(0,255,0,0.7)`
+ `HSL(540,70%,50%)`
`HSLA(540,70%,50%,0.7)`
-Becomes:
+Become:
`#F00`
`#F00A`
@@ -414,7 +416,7 @@ Becomes:
`RGB(0%,100%,0%)`
`RGBA(0,255,0,0.7)`
`HSL(540,70%,50%)`
-`HSLA(540,70%,50%,0.7)`
+`HSLA(540,70%,50%,0.7)`
#### Supported formats:
@@ -481,14 +483,14 @@ Alt-H2
All Markdown-rendered headers automatically get IDs, except in comments.
-On hover a link to those IDs becomes visible to make it easier to copy the link to the header to give it to someone else.
+On hover, a link to those IDs becomes visible to make it easier to copy the link to the header to give it to someone else.
The IDs are generated from the content of the header according to the following rules:
-1. All text is converted to lowercase
-1. All non-word text (e.g., punctuation, HTML) is removed
-1. All spaces are converted to hyphens
-1. Two or more hyphens in a row are converted to one
+1. All text is converted to lowercase.
+1. All non-word text (e.g., punctuation, HTML) is removed.
+1. All spaces are converted to hyphens.
+1. Two or more hyphens in a row are converted to one.
1. If a header with the same ID has already been generated, a unique
incrementing number is appended, starting at 1.
@@ -500,6 +502,7 @@ For example:
# This header has Unicode in it: 한글
## This header has spaces in it
### This header has spaces in it
+## This header has 3.5 in it (and parentheses)
```
Would generate the following link IDs:
@@ -509,11 +512,14 @@ Would generate the following link IDs:
1. `this-header-has-unicode-in-it-한글`
1. `this-header-has-spaces-in-it`
1. `this-header-has-spaces-in-it-1`
+1. `this-header-has-3-5-in-it-and-parentheses`
Note that the Emoji processing happens before the header IDs are generated, so the Emoji is converted to an image which then gets removed from the ID.
### Emphasis
+Examples:
+
```no-highlight
Emphasis, aka italics, with *asterisks* or _underscores_.
@@ -524,6 +530,8 @@ Combined emphasis with **asterisks and _underscores_**.
Strikethrough uses two tildes. ~~Scratch this.~~
```
+Become:
+
Emphasis, aka italics, with *asterisks* or _underscores_.
Strong emphasis, aka bold, with **asterisks** or __underscores__.
@@ -534,12 +542,14 @@ Strikethrough uses two tildes. ~~Scratch this.~~
### Lists
+Examples:
+
```no-highlight
1. First ordered list item
2. Another item
- * Unordered sub-list.
+ * Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
- 1. Ordered sub-list
+ 1. Ordered sub-list
4. And another item.
* Unordered list can use asterisks
@@ -547,11 +557,13 @@ Strikethrough uses two tildes. ~~Scratch this.~~
+ Or pluses
```
+Become:
+
1. First ordered list item
2. Another item
- * Unordered sub-list.
+ * Unordered sub-list.
1. Actual numbers don't matter, just that it's a number
- 1. Ordered sub-list
+ 1. Ordered sub-list
4. And another item.
* Unordered list can use asterisks
@@ -559,33 +571,45 @@ Strikethrough uses two tildes. ~~Scratch this.~~
+ Or pluses
If a list item contains multiple paragraphs,
-each subsequent paragraph should be indented with four spaces.
+each subsequent paragraph should be indented to the same level as the start of the list item text (_Redcarpet: paragraph should be indented with four spaces._)
+
+Example:
```no-highlight
-1. First ordered list item
+1. First ordered list item
- Second paragraph of first item.
-2. Another item
+ Second paragraph of first item.
+
+2. Another item
```
+Becomes:
+
1. First ordered list item
- Second paragraph of first item.
+ Paragraph of first item.
+
2. Another item
-If the second paragraph isn't indented with four spaces,
-the second list item will be incorrectly labeled as `1`.
+If the paragraph of the first item is not indented with the proper number of spaces,
+the paragraph will appear outside the list, instead of properly indented under the list item.
+
+Example:
```no-highlight
1. First ordered list item
- Second paragraph of first item.
+ Paragraph of first item.
+
2. Another item
```
+Becomes:
+
1. First ordered list item
- Second paragraph of first item.
+ Paragraph of first item.
+
2. Another item
### Links
@@ -620,6 +644,8 @@ will point the link to `wikis/style` when the link is inside of a wiki markdown
### Images
+Examples:
+
Here's our logo (hover to see the title text):
Inline-style:
@@ -630,6 +656,8 @@ will point the link to `wikis/style` when the link is inside of a wiki markdown
[logo]: img/markdown_logo.png
+Become:
+
Here's our logo:
Inline-style:
@@ -644,6 +672,8 @@ Reference-style:
### Blockquotes
+Examples:
+
```no-highlight
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
@@ -653,6 +683,8 @@ Quote break.
> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
```
+Become:
+
> Blockquotes are very handy in email to emulate reply text.
> This line is part of the same quote.
@@ -666,6 +698,8 @@ You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubydoc.info/gems/html-pipeline/1.11.0/HTML/Pipeline/SanitizationFilter#WHITELIST-constant) class for the list of allowed HTML tags and attributes. In addition to the default `SanitizationFilter` whitelist, GitLab allows `span`, `abbr`, `details` and `summary` elements.
+Examples:
+
```no-highlight
<dl>
<dt>Definition list</dt>
@@ -676,6 +710,8 @@ See the documentation for HTML::Pipeline's [SanitizationFilter](http://www.rubyd
</dl>
```
+Become:
+
<dl>
<dt>Definition list</dt>
<dd>Is something people use sometimes.</dd>
@@ -691,25 +727,31 @@ Content can be collapsed using HTML's [`<details>`](https://developer.mozilla.or
<p>
<details>
<summary>Click me to collapse/fold.</summary>
-These details will remain hidden until expanded.
+
+These details <em>will</em> remain <strong>hidden</strong> until expanded.
<pre><code>PASTE LOGS HERE</code></pre>
+
</details>
</p>
-**Note:** Unfortunately Markdown is not supported inside these tags, as described by the [markdown specification](https://daringfireball.net/projects/markdown/syntax#html). You can work around this by using HTML, for example you can use `<pre><code>` tags instead of [code fences](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting).
+**Note:** Markdown inside these tags is supported, as long as you have a blank link after the `</summary>` tag and before the `</details>` tag, as shown in the example. _Redcarpet does not support Markdown inside these tags. You can work around this by using HTML, for example you can use `<pre><code>` tags instead of [code fences](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting)._
```html
<details>
<summary>Click me to collapse/fold.</summary>
-These details will remain hidden until expanded.
-<pre><code>PASTE LOGS HERE</code></pre>
+These details _will_ remain **hidden** until expanded.
+
+ PASTE LOGS HERE
+
</details>
```
### Horizontal Rule
+Examples:
+
```
Three or more...
@@ -726,6 +768,8 @@ ___
Underscores
```
+Become:
+
Three or more...
---
@@ -742,10 +786,12 @@ Underscores
### Line Breaks
-My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend.
+A good way to learn how line breaks work is to experiment and discover -- hit <kbd>Enter</kbd> once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. The "Preview" tab is your friend.
Here are some things to try out:
+Examples:
+
```
Here's a line for us to start with.
@@ -760,6 +806,8 @@ This line is *on its own line*, because the previous line ends with two spaces.
spaces.
```
+Become:
+
Here's a line for us to start with.
This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
@@ -774,7 +822,9 @@ spaces.
### Tables
-Tables aren't part of the core Markdown spec, but they are part of GFM and Markdown Here supports them.
+Tables aren't part of the core Markdown spec, but they are part of GFM.
+
+Example:
```
| header 1 | header 2 |
@@ -783,18 +833,18 @@ Tables aren't part of the core Markdown spec, but they are part of GFM and Markd
| cell 3 | cell 4 |
```
-Code above produces next output:
+Becomes:
| header 1 | header 2 |
| -------- | -------- |
| cell 1 | cell 2 |
| cell 3 | cell 4 |
-**Note**
+**Note:** The row of dashes between the table header and body must have at least three dashes in each column.
-The row of dashes between the table header and body must have at least three dashes in each column.
+By including colons in the header row, you can align the text within that column.
-By including colons in the header row, you can align the text within that column:
+Example:
```
| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
@@ -803,6 +853,8 @@ By including colons in the header row, you can align the text within that column
| Cell 7 | Cell 8 | Cell 9 | Cell 10 | Cell 11 | Cell 12 |
```
+Becomes:
+
| Left Aligned | Centered | Right Aligned | Left Aligned | Centered | Right Aligned |
| :----------- | :------: | ------------: | :----------- | :------: | ------------: |
| Cell 1 | Cell 2 | Cell 3 | Cell 4 | Cell 5 | Cell 6 |
@@ -810,13 +862,29 @@ By including colons in the header row, you can align the text within that column
### Footnotes
+Example:
+
```
You can add footnotes to your text as follows.[^2]
[^2]: This is my awesome footnote.
```
+Becomes:
+
You can add footnotes to your text as follows.[^2]
+### Superscripts / Subscripts
+
+CommonMark and GFM currently do not support the superscript syntax ( `x^2` ) that Redcarpet does. You can use the standard HTML syntax for superscripts and subscripts.
+
+```
+The formula for water is H<sub>2</sub>O
+while the equation for the theory of relativity is E = mc<sup>2</sup>.
+```
+
+The formula for water is H<sub>2</sub>O while the equation for the theory of relativity is E = mc<sup>2</sup>.
+
+
## Wiki-specific Markdown
The following examples show how links inside wikis behave.
@@ -908,3 +976,4 @@ A link starting with a `/` is relative to the wiki root.
[katex]: https://github.com/Khan/KaTeX "KaTeX website"
[katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX"
[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
+[commonmarker]: https://github.com/gjtorikian/commonmarker
diff --git a/doc/user/profile/img/profil-preferences-navigation-theme.png b/doc/user/profile/img/profil-preferences-navigation-theme.png
new file mode 100644
index 00000000000..7adaec33b60
--- /dev/null
+++ b/doc/user/profile/img/profil-preferences-navigation-theme.png
Binary files differ
diff --git a/doc/user/profile/img/profile-preferences-syntax-themes.png b/doc/user/profile/img/profile-preferences-syntax-themes.png
new file mode 100644
index 00000000000..719df9410fc
--- /dev/null
+++ b/doc/user/profile/img/profile-preferences-syntax-themes.png
Binary files differ
diff --git a/doc/user/profile/img/profile_settings_dropdown.png b/doc/user/profile/img/profile_settings_dropdown.png
index a2c620642e2..99b06a1bf58 100644
--- a/doc/user/profile/img/profile_settings_dropdown.png
+++ b/doc/user/profile/img/profile_settings_dropdown.png
Binary files differ
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index 930e506802a..e028861a419 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -7,6 +7,34 @@ To navigate to your profile's preferences, click your avatar icon in the top
right corner and select **Settings**. From there on, choose the **Preferences**
tab.
+![Profile preferences settings](img/profile_settings_dropdown.png)
+
+## Navigation theme
+
+>**Note:**
+Navigation themes have been re-introduced with [GitLab 10.0](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/).
+
+The GitLab navigation theme setting allows you to personalize your GitLab experience.
+You can choose from several color themes that add unique colors to the top navigation
+and left side navigation.
+Using individual color themes might help you differentiate between your different
+GitLab instances.
+
+The default palette is Indigo. You can choose between 10 different themes:
+
+- Indigo
+- Light Indigo
+- Blue
+- Light Blue
+- Green
+- Light Green
+- Red
+- Light Red
+- Dark
+- Light
+
+![Profile preferences syntax highlighting themes](img/profile-preferences-syntax-themes.png)
+
## Syntax highlighting theme
>**Note:**
@@ -16,7 +44,7 @@ list of supported languages visit the rouge website.
Changing this setting allows you to customize the color theme when viewing any
syntax highlighted code on GitLab.
-The default one is **White**, and you can choose among 5 different colors:
+The default syntax theme is White, and you can choose among 5 different colors:
- White
- Dark
@@ -24,6 +52,8 @@ The default one is **White**, and you can choose among 5 different colors:
- Solarized dark
- Monokai
+![Profile preferences navigation themes](img/profil-preferences-navigation-theme.png)
+
## Behavior
The following settings allow you to customize the behavior of GitLab's layout
@@ -52,16 +82,16 @@ You have 8 options here that you can use for your default dashboard view:
- Assigned Issues
- Assigned Merge Requests
-### Project home page content
+### Project overview content
-The project home page content setting allows you to choose what content you want to
+The project overview content setting allows you to choose what content you want to
see on a project’s home page.
You can choose between 3 options:
-- Show the files and the readme (default)
-- Show the readme
-- Show the project’s activity
+- Files and Readme (default)
+- Readme
+- Activity
[rouge]: http://rouge.jneen.net/ "Rouge website"
[todos]: ../../workflow/todos.md
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index f50615729dd..2d8fdf0d1da 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -1,14 +1,13 @@
---
-redirect_from: 'https://docs.gitlab.com/ee/user/project/clusters/eks_and_gitlab/index.html'
+author: Joshua Lambert
+author_gitlab: joshlambert
+level: intermediate
+article_type: tutorial
+date: 2018-06-05
---
# Connecting and deploying to an Amazon EKS cluster
-> **[Article Type](../../../../development/writing_documentation.md#types-of-technical-articles):** tutorial ||
-> **Level:** intermediate ||
-> **Author:** [Joshua Lambert](https://gitlab.com/joshlambert) ||
-> **Publication date:** 2018-06-05
-
## Introduction
In this tutorial, we will show how easy it is to integrate an [Amazon EKS](https://aws.amazon.com/eks/) cluster with GitLab, and begin deploying applications.
@@ -39,7 +38,7 @@ Give the project a name, and then select `Create project`.
## Connecting the EKS cluster
-From the left side bar, hover over `CI/CD` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`.
+From the left side bar, hover over `Operations` and select `Kubernetes`, then click on `Add Kubernetes cluster`, and finally `Add an existing Kubernetes cluster`.
A few details from the EKS cluster will be required to connect it to GitLab.
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 1efbe20b84f..48bb2e543c1 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -39,22 +39,22 @@ Before proceeding, make sure the following requirements are met:
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 > Kubernetes** page.
+1. Navigate to your project's **Operations > Kubernetes** page.
1. Click on **Add Kubernetes cluster**.
-1. Click on **Create with GKE**.
+1. Click on **Create with Google Kubernetes Engine**.
1. Connect your Google account if you haven't done already by clicking the
**Sign in with Google** button.
1. Fill in the requested values:
- - **Cluster name** (required) - The name you wish to give the cluster.
- - **GCP project ID** (required) - The ID of the project you created in your GCP
+ - **Kubernetes cluster name** - The name you wish to give the cluster.
+ - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster.
+ - **Google Cloud Platform project** - The project you created in your GCP
console that will host the Kubernetes cluster. This must **not** be confused
- with the project name. Learn more about [Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
+ with the project ID. Learn more about [Google Cloud Platform projects](https://cloud.google.com/resource-manager/docs/creating-managing-projects).
- **Zone** - The [zone](https://cloud.google.com/compute/docs/regions-zones/)
under which the cluster will be created.
- **Number of nodes** - The number of nodes you wish the cluster to have.
- **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
of the Virtual Machine instance that the cluster will be based on.
- - **Environment scope** - The [associated environment](#setting-the-environment-scope) to this cluster.
1. Finally, click the **Create Kubernetes cluster** button.
After a few moments, your cluster should be created. If something goes wrong,
@@ -70,7 +70,7 @@ You need Maintainer [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 > Kubernetes** page.
+1. Navigate to your project's **Operations > Kubernetes** page.
1. Click on **Add Kubernetes 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.
@@ -398,6 +398,10 @@ containers. To use this integration, you should deploy to Kubernetes using
the deployment variables above, ensuring any pods you create are labelled with
`app=$CI_ENVIRONMENT_SLUG`. GitLab will do the rest!
+## Read more
+
+- [Connecting and deploying to an Amazon EKS cluster](eks_and_gitlab/index.md)
+
[permissions]: ../../permissions.md
[ee]: https://about.gitlab.com/products/
[Auto DevOps]: ../../../topics/autodevops/index.md
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index cad85881c4d..fcd6192e82f 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -29,7 +29,9 @@ The following aspects of a project are imported:
* Regular issue and pull request comments
References to pull requests and issues are preserved (GitLab.com & 8.7+), and
-each imported repository defaults to `private` but [can be made public](../settings/index.md#sharing-and-permissions), as needed.
+each imported repository maintains visibility level unless that [visibility
+level is restricted](../../../public_access/public_access.md#restricting-the-use-of-public-or-internal-projects),
+in which case it defaults to the default project visibility.
## How it works
diff --git a/doc/user/project/import/img/import_projects_from_repo_url.png b/doc/user/project/import/img/import_projects_from_repo_url.png
index ec867da1087..c453c7e558a 100644
--- a/doc/user/project/import/img/import_projects_from_repo_url.png
+++ b/doc/user/project/import/img/import_projects_from_repo_url.png
Binary files differ
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index fa7e504c4aa..f687027e8c8 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -30,7 +30,7 @@ GitLab can seamlessly deploy and manage Prometheus on a [connected Kubernetes cl
Once you have a connected Kubernetes cluster with Helm installed, deploying a managed Prometheus is as easy as a single click.
-1. Go to the `CI/CD > Kubernetes` page, to view your connected clusters
+1. Go to the `Operations > 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
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
index 590b1c4275a..a1db79538a4 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
@@ -14,7 +14,7 @@ GitLab has support for automatically detecting and monitoring the Kubernetes NGI
| ---- | ----- |
| Throughput (req/sec) | sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) by (status_code) |
| Latency (ms) | avg(nginx_upstream_response_msecs_avg{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}) |
-| HTTP Error Rate (HTTP Errors / sec) | sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) |
+| HTTP Error Rate (%) | sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100 |
## Configuring NGINX ingress monitoring
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 7eab825fa32..9ca1e6226c5 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -237,23 +237,25 @@ Issue Board, that is create/delete lists and drag issues around.
## Group Issue Board
->Introduced in GitLab 10.6
+> Introduced in [GitLab 10.6](https://about.gitlab.com/2018/03/22/gitlab-10-6-released/#single-group-issue-board-in-core-and-free)
Group issue board is analogous to project-level issue board and it is accessible at the group
navigation level. A group-level issue board allows you to view all issues from all projects in that group or descendant subgroups. Similarly, you can only filter by group labels for these
boards. When updating milestones and labels for an issue through the sidebar update mechanism, again only
group-level objects are available.
+One group issue board per group was made available in GitLab 10.6 Core after multiple group issue boards were originally introduced in [GitLab 10.0 Premium](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards).
+
## Features per tier
Different issue board features are available in different [GitLab tiers](https://about.gitlab.com/pricing/), as shown in the following table:
-| Tier | Number of project issue boards | Board with configuration in project issue boards | Number of group issue boards | Board with configuration in group issue boards |
-| --- | --- | --- | --- | --- |
-| Core | 1 | No | 1 | No |
-| Starter | Multiple | Yes | 1 | No |
-| Premium | Multiple | Yes | Multiple | Yes |
-| Ultimate | Multiple | Yes | Multiple | Yes |
+| Tier | Number of Project Issue Boards | Number of Group Issue Boards | Configurable Project Issue Boards | Configurable Group Issue Boards | Assignee Lists
+| --- | --- | --- | --- | --- | --- |
+| Core | 1 | 1 | No | No | No |
+| Starter | Multiple | 1 | Yes | No | No |
+| Premium | Multiple | Multiple | Yes | Yes | Yes |
+| Ultimate | Multiple | Multiple | Yes | Yes | Yes |
## Tips
diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md
index 2f4ed3493c2..0ef8eddad20 100644
--- a/doc/user/project/quick_actions.md
+++ b/doc/user/project/quick_actions.md
@@ -42,3 +42,4 @@ do.
| `/tableflip` | Append the comment with `(╯°□°)╯︵ ┻━┻` |
| `/shrug` | Append the comment with `¯\_(ツ)_/¯` |
| <code>/copy_metadata #issue &#124; !merge_request</code> | Copy labels and milestone from other issue or merge request |
+| `/confidential` | Makes the issue confidential | \ No newline at end of file
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 105d8a6ab61..b0143e45ab6 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -42,5 +42,26 @@ list.
An additional review mode is available when you open a merge request, which
shows you a preview of the merge request diff if you commit your changes.
+## View CI job logs
+
+> [Introduced in](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19279) [GitLab Core][ce] 11.0.
+
+The Web IDE can be used to quickly fix failing tests by opening the branch or
+merge request in the Web IDE and opening the logs of the failed job. The status
+of all jobs for the most recent pipeline and job traces for the current commit
+can be accessed by clicking the **Pipelines** button in the top right.
+
+The pipeline status is also shown at all times in the status bar in the bottom
+left.
+
+## Switching merge requests
+
+> [Introduced in](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/19318) [GitLab Core][ce] 11.0.
+
+Switching between your authored and assigned merge requests can be done without
+leaving the Web IDE. Click the project name in the top left to open a list of
+merge requests. You will need to commit or discard all your changes before
+switching to a different merge request.
+
[ce]: https://about.gitlab.com/pricing/
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index 50ec99be48b..6c1378560ef 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -58,7 +58,7 @@ Currently the following names are reserved as top level groups:
- dashboard
- deploy.html
- explore
-- favicon.ico
+- favicon.png
- groups
- header_logo_dark.png
- header_logo_light.png
diff --git a/doc/workflow/notifications.md b/doc/workflow/notifications.md
index fc592b99860..edb0c6bdc30 100644
--- a/doc/workflow/notifications.md
+++ b/doc/workflow/notifications.md
@@ -34,9 +34,14 @@ anything that is set at Global Settings.
![notification settings](img/notification_group_settings.png)
-Group Settings are taking precedence over Global Settings but are on a level below Project Settings.
+Group Settings are taking precedence over Global Settings but are on a level below Project or Subgroup Settings:
+
+```
+Group < Subgroup < Project
+```
+
This means that you can set a different level of notifications per group while still being able
-to have a finer level setting per project.
+to have a finer level setting per project or subgroup.
Organization like this is suitable for users that belong to different groups but don't have the
same need for being notified for every group they are member of.
These settings can be configured on group page under the name of the group. It will be the dropdown with the bell icon. They can also be configured on the user profile notifications dropdown.
diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md
index aaddbe4fbf5..8c4e6ea8eab 100644
--- a/doc/workflow/repository_mirroring.md
+++ b/doc/workflow/repository_mirroring.md
@@ -228,7 +228,7 @@ backoff period.
If the mirror fails (eg: branch diverged from upstream), the project's backoff
period will be penalized each time it fails up to a maximum amount of time.
-## Pushing to a remote repository **[STARTER]**
+## Pushing to a remote repository
>[Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/249) in
GitLab Enterprise Edition 8.7. [Moved to GitLab Community Edition][ce-18715] in 10.8.
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 684955a1b24..964780cba6a 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -15,19 +15,21 @@ module API
end
params do
optional :ref_name, type: String, desc: 'The name of a repository branch or tag, if not given the default branch is used'
- optional :since, type: DateTime, desc: 'Only commits after or on this date will be returned'
- optional :until, type: DateTime, desc: 'Only commits before or on this date will be returned'
- optional :path, type: String, desc: 'The file path'
- optional :all, type: Boolean, desc: 'Every commit will be returned'
+ optional :since, type: DateTime, desc: 'Only commits after or on this date will be returned'
+ optional :until, type: DateTime, desc: 'Only commits before or on this date will be returned'
+ optional :path, type: String, desc: 'The file path'
+ optional :all, type: Boolean, desc: 'Every commit will be returned'
+ optional :with_stats, type: Boolean, desc: 'Stats about each commit will be added to the response'
use :pagination
end
get ':id/repository/commits' do
- path = params[:path]
+ path = params[:path]
before = params[:until]
- after = params[:since]
- ref = params[:ref_name] || user_project.try(:default_branch) || 'master' unless params[:all]
+ after = params[:since]
+ ref = params[:ref_name] || user_project.try(:default_branch) || 'master' unless params[:all]
offset = (params[:page] - 1) * params[:per_page]
- all = params[:all]
+ all = params[:all]
+ with_stats = params[:with_stats]
commits = user_project.repository.commits(ref,
path: path,
@@ -47,7 +49,9 @@ module API
paginated_commits = Kaminari.paginate_array(commits, total_count: commit_count)
- present paginate(paginated_commits), with: Entities::Commit
+ serializer = with_stats ? Entities::CommitWithStats : Entities::Commit
+
+ present paginate(paginated_commits), with: serializer
end
desc 'Commit multiple file changes as one commit' do
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 22afcb9edf2..1cc8fcb8408 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -308,6 +308,10 @@ module API
expose :additions, :deletions, :total
end
+ class CommitWithStats < Commit
+ expose :stats, using: Entities::CommitStats
+ end
+
class CommitDetail < Commit
expose :stats, using: Entities::CommitStats, if: :stats
expose :status
@@ -358,7 +362,7 @@ module API
end
class Snippet < Grape::Entity
- expose :id, :title, :file_name, :description
+ expose :id, :title, :file_name, :description, :visibility
expose :author, using: Entities::UserBasic
expose :updated_at, :created_at
expose :project_id
@@ -412,6 +416,10 @@ module API
expose :state, :created_at, :updated_at
expose :due_date
expose :start_date
+
+ expose :web_url do |milestone, _options|
+ Gitlab::UrlBuilder.build(milestone)
+ end
end
class IssueBasic < ProjectEntity
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 2ed331d4fd2..9c53b7c3fe7 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -272,7 +272,8 @@ module API
attrs[key] = params_hash[key]
end
end
- ActionController::Parameters.new(attrs).permit!
+ permitted_attrs = ActionController::Parameters.new(attrs).permit!
+ Gitlab.rails5? ? permitted_attrs.to_h : permitted_attrs
end
def filter_by_iid(items, iid)
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 35ac0b4cbca..61eb88d3331 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -59,6 +59,11 @@ module API
def max_artifacts_size
Gitlab::CurrentSettings.max_artifacts_size.megabytes.to_i
end
+
+ def job_forbidden!(job, reason)
+ header 'Job-Status', job.status
+ forbidden!(reason)
+ end
end
end
end
diff --git a/lib/api/markdown.rb b/lib/api/markdown.rb
index b9ed68aa584..5d55224c1a7 100644
--- a/lib/api/markdown.rb
+++ b/lib/api/markdown.rb
@@ -10,9 +10,7 @@ module API
detail "This feature was introduced in GitLab 11.0."
end
post do
- # Explicitly set CommonMark as markdown engine to use.
- # Remove this set when https://gitlab.com/gitlab-org/gitlab-ce/issues/43011 is done.
- context = { markdown_engine: :common_mark, only_path: false }
+ context = { only_path: false }
if params[:project]
project = Project.find_by_full_path(params[:project])
diff --git a/lib/api/runner.rb b/lib/api/runner.rb
index db502697a19..dc102259ca8 100644
--- a/lib/api/runner.rb
+++ b/lib/api/runner.rb
@@ -125,7 +125,7 @@ module API
end
put '/:id' do
job = authenticate_job!
- forbidden!('Job is not running') unless job.running?
+ job_forbidden!(job, 'Job is not running') unless job.running?
job.trace.set(params[:trace]) if params[:trace]
@@ -133,6 +133,8 @@ module API
project: job.project.full_path)
case params[:state].to_s
+ when 'running'
+ job.touch if job.needs_touch?
when 'success'
job.success!
when 'failed'
@@ -152,7 +154,7 @@ module API
end
patch '/:id/trace' do
job = authenticate_job!
- forbidden!('Job is not running') unless job.running?
+ job_forbidden!(job, 'Job is not running') unless job.running?
error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range')
content_range = request.headers['Content-Range']
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 14b8a796c8e..e8df2c5a74a 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -531,18 +531,22 @@ module API
authenticate!
end
- desc 'Get the currently authenticated user' do
- success Entities::UserPublic
- end
- get do
- entity =
- if current_user.admin?
- Entities::UserWithAdmin
- else
- Entities::UserPublic
- end
+ # Enabling /user endpoint for the v3 version to allow oauth
+ # authentication through this endpoint.
+ version %w(v3 v4), using: :path do
+ desc 'Get the currently authenticated user' do
+ success Entities::UserPublic
+ end
+ get do
+ entity =
+ if current_user.admin?
+ Entities::UserWithAdmin
+ else
+ Entities::UserPublic
+ end
- present current_user, with: entity
+ present current_user, with: entity
+ end
end
desc "Get the currently authenticated user's SSH keys" do
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
index d769a3ee7b0..e287aa1e392 100644
--- a/lib/backup/files.rb
+++ b/lib/backup/files.rb
@@ -29,10 +29,10 @@ module Backup
raise Backup::Error, 'Backup failed'
end
- run_pipeline!([%W(tar --exclude=lost+found -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
+ run_pipeline!([%W(#{tar} --exclude=lost+found -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
FileUtils.rm_rf(@backup_files_dir)
else
- run_pipeline!([%W(tar --exclude=lost+found -C #{app_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
+ run_pipeline!([%W(#{tar} --exclude=lost+found -C #{app_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
end
end
@@ -43,7 +43,12 @@ module Backup
end
def tar
- system(*%w[gtar --version], out: '/dev/null') ? 'gtar' : 'tar'
+ if system(*%w[gtar --version], out: '/dev/null')
+ # It looks like we can get GNU tar by running 'gtar'
+ 'gtar'
+ else
+ 'tar'
+ end
end
def backup_existing_files_dir
diff --git a/lib/backup/repository.rb b/lib/backup/repository.rb
index 1b1c83d9fb3..0119c5d6851 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -112,18 +112,31 @@ module Backup
end
end
+ def local_restore_custom_hooks(project, dir)
+ path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ path_to_repo(project)
+ end
+ cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
+ output, status = Gitlab::Popen.popen(cmd)
+ unless status.zero?
+ progress_warn(project, cmd.join(' '), output)
+ end
+ end
+
+ def gitaly_restore_custom_hooks(project, dir)
+ custom_hooks_path = path_to_tars(project, dir)
+ Gitlab::GitalyClient::RepositoryService.new(project.repository)
+ .restore_custom_hooks(custom_hooks_path)
+ end
+
def restore_custom_hooks(project)
- # TODO: Need to find a way to do this for gitaly
- # Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195
in_path(path_to_tars(project)) do |dir|
- path_to_project_repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- path_to_repo(project)
- end
- cmd = %W(tar -xf #{path_to_tars(project, dir)} -C #{path_to_project_repo} #{dir})
-
- output, status = Gitlab::Popen.popen(cmd)
- unless status.zero?
- progress_warn(project, cmd.join(' '), output)
+ gitaly_migrate(:restore_custom_hooks) do |is_enabled|
+ if is_enabled
+ local_restore_custom_hooks(project, dir)
+ else
+ gitaly_restore_custom_hooks(project, dir)
+ end
end
end
end
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index c1e2b680240..944363f17d3 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -14,7 +14,7 @@ module Banzai
private
- DEFAULT_ENGINE = :redcarpet
+ DEFAULT_ENGINE = :common_mark
def engine(engine_from_context)
engine_from_context ||= DEFAULT_ENGINE
diff --git a/lib/banzai/filter/milestone_reference_filter.rb b/lib/banzai/filter/milestone_reference_filter.rb
index b144bd8cf54..858e790005c 100644
--- a/lib/banzai/filter/milestone_reference_filter.rb
+++ b/lib/banzai/filter/milestone_reference_filter.rb
@@ -65,7 +65,7 @@ module Banzai
# We don't support IID lookups for group milestones, because IIDs can
# clash between group and project milestones.
if project.group && !params[:iid]
- finder_params[:group_ids] = [project.group.id]
+ finder_params[:group_ids] = project.group.self_and_ancestors.select(:id)
end
MilestonesFinder.new(finder_params).find_by(params)
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index a129746e2c6..b9a148f35bf 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -33,6 +33,7 @@ module Gitlab
APP_DIRS_PATTERN = %r{^/?(app|config|ee|lib|spec|\(\w*\))}
SUBDOMAIN_REGEX = %r{\Ahttps://[a-z0-9]+\.gitlab\.com\z}
VERSION = File.read(root.join("VERSION")).strip.freeze
+ INSTALLATION_TYPE = File.read(root.join("INSTALLATION_TYPE")).strip.freeze
def self.com?
# Check `gl_subdomain?` as well to keep parity with gitlab.com
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 6cf7aa1bf0d..3cf35f499cd 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -24,7 +24,22 @@ module Gitlab
private
def ensure_application_settings!
+ cached_application_settings || uncached_application_settings
+ end
+
+ def cached_application_settings
return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
+
+ begin
+ ::ApplicationSetting.cached
+ rescue
+ # In case Redis isn't running
+ # or the Redis UNIX socket file is not available
+ # or the DB is not running (we use migrations in the cache key)
+ end
+ end
+
+ def uncached_application_settings
return fake_application_settings unless connect_to_db?
current_settings = ::ApplicationSetting.current
diff --git a/lib/gitlab/cycle_analytics/summary/commit.rb b/lib/gitlab/cycle_analytics/summary/commit.rb
index 0a88e052f60..550c1755a71 100644
--- a/lib/gitlab/cycle_analytics/summary/commit.rb
+++ b/lib/gitlab/cycle_analytics/summary/commit.rb
@@ -7,9 +7,7 @@ module Gitlab
end
def value
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- @value ||= count_commits
- end
+ @value ||= count_commits
end
private
@@ -21,19 +19,11 @@ module Gitlab
def count_commits
return unless ref
- repository = @project.repository.raw_repository
- sha = @project.repository.commit(ref).sha
-
- cmd = %W(git --git-dir=#{repository.path} log)
- cmd << '--format=%H'
- cmd << "--after=#{@from.iso8601}"
- cmd << sha
-
- output, status = Gitlab::Popen.popen(cmd)
-
- raise IOError, output unless status.zero?
+ gitaly_commit_client.commit_count(ref, after: @from)
+ end
- output.lines.count
+ def gitaly_commit_client
+ Gitlab::GitalyClient::CommitService.new(@project.repository.raw_repository)
end
def ref
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
new file mode 100644
index 00000000000..451c9daf761
--- /dev/null
+++ b/lib/gitlab/favicon.rb
@@ -0,0 +1,47 @@
+module Gitlab
+ class Favicon
+ class << self
+ def main
+ return appearance_favicon.favicon_main.url if appearance_favicon.exists?
+
+ image_name =
+ if Gitlab::Utils.to_boolean(ENV['CANARY'])
+ 'favicon-yellow.png'
+ elsif Rails.env.development?
+ 'favicon-blue.png'
+ else
+ 'favicon.png'
+ end
+
+ ActionController::Base.helpers.image_path(image_name)
+ end
+
+ def status_overlay(status_name)
+ path = File.join(
+ 'ci_favicons',
+ "#{status_name}.png"
+ )
+
+ ActionController::Base.helpers.image_path(path)
+ end
+
+ def available_status_names
+ @available_status_names ||= begin
+ Dir.glob(Rails.root.join('app', 'assets', 'images', 'ci_favicons', '*.png'))
+ .map { |file| File.basename(file, '.png') }
+ .sort
+ end
+ end
+
+ private
+
+ def appearance
+ RequestStore.store[:appearance] ||= (Appearance.current || Appearance.new)
+ end
+
+ def appearance_favicon
+ appearance.favicon
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git.rb b/lib/gitlab/git.rb
index e85e87a54af..55236a1122f 100644
--- a/lib/gitlab/git.rb
+++ b/lib/gitlab/git.rb
@@ -62,7 +62,7 @@ module Gitlab
end
def version
- Gitlab::VersionInfo.parse(Gitlab::Popen.popen(%W(#{Gitlab.config.git.bin_path} --version)).first)
+ Gitlab::Git::Version.git_version
end
def check_namespace!(*objects)
diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb
index 4158d50cd9e..e25e15f5c80 100644
--- a/lib/gitlab/git/blame.rb
+++ b/lib/gitlab/git/blame.rb
@@ -22,24 +22,9 @@ module Gitlab
private
def load_blame
- raw_output = @repo.gitaly_migrate(:blame) do |is_enabled|
- if is_enabled
- load_blame_by_gitaly
- else
- load_blame_by_shelling_out
- end
- end
-
- output = encode_utf8(raw_output)
- process_raw_blame output
- end
-
- def load_blame_by_gitaly
- @repo.gitaly_commit_client.raw_blame(@sha, @path)
- end
+ output = encode_utf8(@repo.gitaly_commit_client.raw_blame(@sha, @path))
- def load_blame_by_shelling_out
- @repo.shell_blame(@sha, @path)
+ process_raw_blame(output)
end
def process_raw_blame(output)
diff --git a/lib/gitlab/git/committer_with_hooks.rb b/lib/gitlab/git/committer_with_hooks.rb
index a8a59f998cd..4198be7c9c9 100644
--- a/lib/gitlab/git/committer_with_hooks.rb
+++ b/lib/gitlab/git/committer_with_hooks.rb
@@ -20,7 +20,7 @@ module Gitlab
end
result[:newrev]
- rescue Gitlab::Git::HooksService::PreReceiveError => e
+ rescue Gitlab::Git::PreReceiveError => e
message = "Custom Hook failed: #{e.message}"
raise Gitlab::Git::Wiki::OperationError, message
end
diff --git a/lib/gitlab/git/gitlab_projects.rb b/lib/gitlab/git/gitlab_projects.rb
index 00c943fdb25..8475645971e 100644
--- a/lib/gitlab/git/gitlab_projects.rb
+++ b/lib/gitlab/git/gitlab_projects.rb
@@ -53,24 +53,11 @@ module Gitlab
# Import project via git clone --bare
# URL must be publicly cloneable
def import_project(source, timeout)
- Gitlab::GitalyClient.migrate(:import_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_import_repository(source)
- else
- git_import_repository(source, timeout)
- end
- end
+ git_import_repository(source, timeout)
end
def fork_repository(new_shard_name, new_repository_relative_path)
- Gitlab::GitalyClient.migrate(:fork_repository,
- status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_fork_repository(new_shard_name, new_repository_relative_path)
- else
- git_fork_repository(new_shard_name, new_repository_relative_path)
- end
- end
+ git_fork_repository(new_shard_name, new_repository_relative_path)
end
def fetch_remote(name, timeout, force:, tags:, ssh_key: nil, known_hosts: nil, prune: true)
@@ -241,16 +228,6 @@ module Gitlab
true
end
- def gitaly_import_repository(source)
- raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil)
-
- Gitlab::GitalyClient::RepositoryService.new(raw_repository).import_repository(source)
- true
- rescue GRPC::BadStatus => e
- @output << e.message
- false
- end
-
def git_fork_repository(new_shard_name, new_repository_relative_path)
from_path = repository_absolute_path
new_shard_path = Gitlab.config.repositories.storages.fetch(new_shard_name).legacy_disk_path
@@ -270,16 +247,6 @@ module Gitlab
run(cmd, nil) && Gitlab::Git::Repository.create_hooks(to_path, global_hooks_path)
end
-
- def gitaly_fork_repository(new_shard_name, new_repository_relative_path)
- target_repository = Gitlab::Git::Repository.new(new_shard_name, new_repository_relative_path, nil)
- raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil)
-
- Gitlab::GitalyClient::RepositoryService.new(target_repository).fork_repository(raw_repository)
- rescue GRPC::BadStatus => e
- logger.error "fork-repository failed: #{e.message}"
- false
- end
end
end
end
diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb
index 7c201c6169b..94ff5b4980a 100644
--- a/lib/gitlab/git/hook.rb
+++ b/lib/gitlab/git/hook.rb
@@ -11,7 +11,7 @@ module Gitlab
def initialize(name, repository)
@name = name
@repository = repository
- @path = File.join(repo_path.strip, 'hooks', name)
+ @path = File.join(repo_path, 'hooks', name)
end
def repo_path
@@ -95,13 +95,13 @@ module Gitlab
args = [ref, oldrev, newrev]
stdout, stderr, status = Open3.capture3(env, path, *args, options)
- [status.success?, Gitlab::Utils.nlbr(stderr.presence || stdout)]
+ [status.success?, stderr.presence || stdout]
end
def retrieve_error_message(stderr, stdout)
err_message = stderr.read
err_message = err_message.blank? ? stdout.read : err_message
- Gitlab::Utils.nlbr(err_message)
+ err_message
end
end
end
diff --git a/lib/gitlab/git/hooks_service.rb b/lib/gitlab/git/hooks_service.rb
index f302b852b35..e67cacdb95a 100644
--- a/lib/gitlab/git/hooks_service.rb
+++ b/lib/gitlab/git/hooks_service.rb
@@ -1,8 +1,6 @@
module Gitlab
module Git
class HooksService
- PreReceiveError = Class.new(StandardError)
-
attr_accessor :oldrev, :newrev, :ref
def execute(pusher, repository, oldrev, newrev, ref)
diff --git a/lib/gitlab/git/lfs_changes.rb b/lib/gitlab/git/lfs_changes.rb
index b9e5cf258f4..f3cc388ea41 100644
--- a/lib/gitlab/git/lfs_changes.rb
+++ b/lib/gitlab/git/lfs_changes.rb
@@ -30,7 +30,7 @@ module Gitlab
def git_new_pointers(object_limit, not_in)
@new_pointers ||= begin
- rev_list.new_objects(not_in: not_in, require_path: true) do |object_ids|
+ rev_list.new_objects(rev_list_params(not_in: not_in)) do |object_ids|
object_ids = object_ids.take(object_limit) if object_limit
Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
@@ -39,7 +39,12 @@ module Gitlab
end
def git_all_pointers
- rev_list.all_objects(require_path: true) do |object_ids|
+ params = {}
+ if rev_list_supports_new_options?
+ params[:options] = ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"]
+ end
+
+ rev_list.all_objects(rev_list_params(params)) do |object_ids|
Gitlab::Git::Blob.batch_lfs_pointers(@repository, object_ids)
end
end
@@ -47,6 +52,23 @@ module Gitlab
def rev_list
Gitlab::Git::RevList.new(@repository, newrev: @newrev)
end
+
+ # We're passing the `--in-commit-order` arg to ensure we don't wait
+ # for git to traverse all commits before returning pointers.
+ # This is required in order to improve the performance of LFS integrity check
+ def rev_list_params(params = {})
+ params[:options] ||= []
+ params[:options] << "--in-commit-order" if rev_list_supports_new_options?
+ params[:require_path] = true
+
+ params
+ end
+
+ def rev_list_supports_new_options?
+ return @option_supported if defined?(@option_supported)
+
+ @option_supported = Gitlab::Git.version >= Gitlab::VersionInfo.parse('2.16.0')
+ end
end
end
end
diff --git a/lib/gitlab/git/pre_receive_error.rb b/lib/gitlab/git/pre_receive_error.rb
new file mode 100644
index 00000000000..ac1ab7c39d5
--- /dev/null
+++ b/lib/gitlab/git/pre_receive_error.rb
@@ -0,0 +1,21 @@
+module Gitlab
+ module Git
+ #
+ # PreReceiveError is special because its message gets displayed to users
+ # in the web UI. To prevent XSS we sanitize the message on
+ # initialization.
+ class PreReceiveError < StandardError
+ def initialize(msg = '')
+ super(nlbr(msg))
+ end
+
+ private
+
+ # In gitaly-ruby we override this method to do nothing, so that
+ # sanitization happens in gitlab-rails only.
+ def nlbr(str)
+ Gitlab::Utils.nlbr(str)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 93f9adaf1f1..eb5d6318dcb 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -120,13 +120,11 @@ module Gitlab
# Default branch in the repository
def root_ref
- @root_ref ||= gitaly_migrate(:root_ref) do |is_enabled|
- if is_enabled
- gitaly_ref_client.default_branch_name
- else
- discover_default_branch
- end
- end
+ gitaly_ref_client.default_branch_name
+ rescue GRPC::NotFound => e
+ raise NoRepository.new(e.message)
+ rescue GRPC::Unknown => e
+ raise Gitlab::Git::CommandError.new(e.message)
end
def rugged
@@ -152,23 +150,15 @@ module Gitlab
# Returns an Array of branch names
# sorted by name ASC
def branch_names
- gitaly_migrate(:branch_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.branch_names
- else
- branches.map(&:name)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.branch_names
end
end
# Returns an Array of Branches
def branches
- gitaly_migrate(:branches) do |is_enabled|
- if is_enabled
- gitaly_ref_client.branches
- else
- branches_filter
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.branches
end
end
@@ -200,12 +190,8 @@ module Gitlab
end
def local_branches(sort_by: nil)
- gitaly_migrate(:local_branches) do |is_enabled|
- if is_enabled
- gitaly_ref_client.local_branches(sort_by: sort_by)
- else
- branches_filter(filter: :local, sort_by: sort_by)
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.local_branches(sort_by: sort_by)
end
end
@@ -245,18 +231,6 @@ module Gitlab
# This refs by default not visible in project page and not cloned to client side.
alias_method :has_visible_content?, :has_local_branches?
- def has_local_branches_rugged?
- rugged.branches.each(:local).any? do |ref|
- begin
- ref.name && ref.target # ensures the branch is valid
-
- true
- rescue Rugged::ReferenceError
- false
- end
- end
- end
-
# Returns the number of valid tags
def tag_count
gitaly_migrate(:tag_names) do |is_enabled|
@@ -270,12 +244,8 @@ module Gitlab
# Returns an Array of tag names
def tag_names
- gitaly_migrate(:tag_names) do |is_enabled|
- if is_enabled
- gitaly_ref_client.tag_names
- else
- rugged.tags.map { |t| t.name }
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.tag_names
end
end
@@ -283,12 +253,8 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/390
def tags
- gitaly_migrate(:tags) do |is_enabled|
- if is_enabled
- tags_from_gitaly
- else
- tags_from_rugged
- end
+ wrapped_gitaly_errors do
+ gitaly_ref_client.tags
end
end
@@ -310,7 +276,7 @@ module Gitlab
#
# name - The name of the tag as a String.
def tag_exists?(name)
- gitaly_migrate(:ref_exists_tags) do |is_enabled|
+ gitaly_migrate(:ref_exists_tags, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_exists?("refs/tags/#{name}")
else
@@ -323,7 +289,7 @@ module Gitlab
#
# name - The name of the branch as a String.
def branch_exists?(name)
- gitaly_migrate(:ref_exists_branches) do |is_enabled|
+ gitaly_migrate(:ref_exists_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_ref_exists?("refs/heads/#{name}")
else
@@ -364,31 +330,6 @@ module Gitlab
end.map(&:name)
end
- # Discovers the default branch based on the repository's available branches
- #
- # - If no branches are present, returns nil
- # - If one branch is present, returns its name
- # - If two or more branches are present, returns current HEAD or master or first branch
- def discover_default_branch
- names = branch_names
-
- return if names.empty?
-
- return names[0] if names.length == 1
-
- if rugged_head
- extracted_name = Ref.extract_branch_name(rugged_head.name)
-
- return extracted_name if names.include?(extracted_name)
- end
-
- if names.include?('master')
- 'master'
- else
- names[0]
- end
- end
-
def rugged_head
rugged.head
rescue Rugged::ReferenceError
@@ -533,7 +474,7 @@ module Gitlab
def count_commits(options)
count_commits_options = process_count_commits_options(options)
- gitaly_migrate(:count_commits) do |is_enabled|
+ gitaly_migrate(:count_commits, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
count_commits_by_gitaly(count_commits_options)
else
@@ -738,7 +679,7 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/330
def commit_count(ref)
- gitaly_migrate(:commit_count) do |is_enabled|
+ gitaly_migrate(:commit_count, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
if is_enabled
gitaly_commit_client.commit_count(ref)
else
@@ -1181,18 +1122,18 @@ module Gitlab
end
def compare_source_branch(target_branch_name, source_repository, source_branch_name, straight:)
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- with_repo_branch_commit(source_repository, source_branch_name) do |commit|
- break unless commit
+ tmp_ref = "refs/tmp/#{SecureRandom.hex}"
- Gitlab::Git::Compare.new(
- self,
- target_branch_name,
- commit.sha,
- straight: straight
- )
- end
- end
+ return unless fetch_source_branch!(source_repository, source_branch_name, tmp_ref)
+
+ Gitlab::Git::Compare.new(
+ self,
+ target_branch_name,
+ tmp_ref,
+ straight: straight
+ )
+ ensure
+ delete_refs(tmp_ref)
end
def write_ref(ref_path, ref, old_ref: nil, shell: true)
@@ -1453,6 +1394,16 @@ module Gitlab
raise CommandError.new(e)
end
+ def wrapped_gitaly_errors(&block)
+ yield block
+ rescue GRPC::NotFound => e
+ raise NoRepository.new(e)
+ rescue GRPC::InvalidArgument => e
+ raise ArgumentError.new(e)
+ rescue GRPC::BadStatus => e
+ raise CommandError.new(e)
+ end
+
def clean_stale_repository_files
gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
gitaly_repository_client.cleanup if is_enabled && exists?
@@ -1606,12 +1557,8 @@ module Gitlab
private
def uncached_has_local_branches?
- gitaly_migrate(:has_local_branches, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
- if is_enabled
- gitaly_repository_client.has_local_branches?
- else
- has_local_branches_rugged?
- end
+ wrapped_gitaly_errors do
+ gitaly_repository_client.has_local_branches?
end
end
@@ -1731,20 +1678,6 @@ module Gitlab
}
end
- # 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)
- 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
-
def git_merged_branch_names(branch_names, root_sha)
git_arguments =
%W[branch --merged #{root_sha}
@@ -1956,37 +1889,11 @@ module Gitlab
end
end
- def tags_from_rugged
- rugged.references.each("refs/tags/*").map do |ref|
- message = nil
-
- if ref.target.is_a?(Rugged::Tag::Annotation)
- tag_message = ref.target.message
-
- if tag_message.respond_to?(:chomp)
- message = tag_message.chomp
- end
- end
-
- target_commit = Gitlab::Git::Commit.find(self, ref.target)
- Gitlab::Git::Tag.new(self, {
- name: ref.name,
- target: ref.target,
- target_commit: target_commit,
- message: message
- })
- end.sort_by(&:name)
- end
-
def last_commit_for_path_by_rugged(sha, path)
sha = last_commit_id_for_path_by_shelling_out(sha, path)
commit(sha)
end
- def tags_from_gitaly
- gitaly_ref_client.tags
- end
-
def size_by_shelling_out
popen(%w(du -sk), path).first.strip.to_i
end
diff --git a/lib/gitlab/git/rev_list.rb b/lib/gitlab/git/rev_list.rb
index 79544ccf13d..4e661eceffb 100644
--- a/lib/gitlab/git/rev_list.rb
+++ b/lib/gitlab/git/rev_list.rb
@@ -27,9 +27,10 @@ module Gitlab
#
# When given a block it will yield objects as a lazy enumerator so
# the caller can limit work done instead of processing megabytes of data
- def new_objects(require_path: nil, not_in: nil, &lazy_block)
+ def new_objects(options: [], require_path: nil, not_in: nil, &lazy_block)
opts = {
including: newrev,
+ options: options,
excluding: not_in.nil? ? :all : not_in,
require_path: require_path
}
@@ -37,9 +38,9 @@ module Gitlab
get_objects(opts, &lazy_block)
end
- def all_objects(require_path: nil, &lazy_block)
+ def all_objects(options: [], require_path: nil, &lazy_block)
get_objects(including: :all,
- options: ["--filter=blob:limit=#{Gitlab::Git::Blob::LFS_POINTER_MAX_SIZE}"],
+ options: options,
require_path: require_path,
&lazy_block)
end
diff --git a/lib/gitlab/git/version.rb b/lib/gitlab/git/version.rb
new file mode 100644
index 00000000000..11184ca3457
--- /dev/null
+++ b/lib/gitlab/git/version.rb
@@ -0,0 +1,11 @@
+module Gitlab
+ module Git
+ module Version
+ extend Gitlab::Git::Popen
+
+ def self.git_version
+ Gitlab::VersionInfo.parse(popen(%W(#{Gitlab.config.git.bin_path} --version), nil).first)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 36e9adf27da..620362b52a9 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -33,11 +33,6 @@ module Gitlab
MAXIMUM_GITALY_CALLS = 35
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
- # We have a mechanism to let GitLab automatically opt in to all Gitaly
- # features. We want to be able to exclude some features from automatic
- # opt-in. That is what EXPLICIT_OPT_IN_REQUIRED is for.
- EXPLICIT_OPT_IN_REQUIRED = [Gitlab::GitalyClient::StorageSettings::DISK_ACCESS_DENIED_FLAG].freeze
-
MUTEX = Mutex.new
class << self
@@ -249,7 +244,7 @@ module Gitlab
when MigrationStatus::OPT_OUT
true
when MigrationStatus::OPT_IN
- opt_into_all_features? && !EXPLICIT_OPT_IN_REQUIRED.include?(feature_name)
+ opt_into_all_features? && !explicit_opt_in_required.include?(feature_name)
else
false
end
@@ -259,6 +254,13 @@ module Gitlab
false
end
+ # We have a mechanism to let GitLab automatically opt in to all Gitaly
+ # features. We want to be able to exclude some features from automatic
+ # opt-in. This function has an override in EE.
+ def self.explicit_opt_in_required
+ []
+ end
+
# opt_into_all_features? returns true when the current environment
# is one in which we opt into features automatically
def self.opt_into_all_features?
diff --git a/lib/gitlab/gitaly_client/commit_service.rb b/lib/gitlab/gitaly_client/commit_service.rb
index a4cc64de80d..7f2e6441f16 100644
--- a/lib/gitlab/gitaly_client/commit_service.rb
+++ b/lib/gitlab/gitaly_client/commit_service.rb
@@ -179,6 +179,8 @@ module Gitlab
end
def list_commits_by_oid(oids)
+ return [] if oids.empty?
+
request = Gitaly::ListCommitsByOidRequest.new(repository: @gitaly_repo, oid: oids)
response = GitalyClient.call(@repository.storage, :commit_service, :list_commits_by_oid, request, timeout: GitalyClient.medium_timeout)
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 44b0e517bf0..e9d4bb4c4b6 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -20,7 +20,7 @@ module Gitlab
response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_tag, request)
if pre_receive_error = response.pre_receive_error.presence
- raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
end
end
@@ -35,7 +35,7 @@ module Gitlab
response = GitalyClient.call(@repository.storage, :operation_service, :user_create_tag, request)
if pre_receive_error = response.pre_receive_error.presence
- raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
elsif response.exists
raise Gitlab::Git::Repository::TagExistsError
end
@@ -56,7 +56,7 @@ module Gitlab
:user_create_branch, request)
if response.pre_receive_error.present?
- raise Gitlab::Git::HooksService::PreReceiveError.new(response.pre_receive_error)
+ raise Gitlab::Git::PreReceiveError.new(response.pre_receive_error)
end
branch = response.branch
@@ -76,7 +76,7 @@ module Gitlab
response = GitalyClient.call(@repository.storage, :operation_service, :user_delete_branch, request)
if pre_receive_error = response.pre_receive_error.presence
- raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
end
end
@@ -106,7 +106,7 @@ module Gitlab
second_response = response_enum.next
if second_response.pre_receive_error.present?
- raise Gitlab::Git::HooksService::PreReceiveError, second_response.pre_receive_error
+ raise Gitlab::Git::PreReceiveError, second_response.pre_receive_error
end
branch_update = second_response.branch_update
@@ -175,7 +175,7 @@ module Gitlab
)
if response.pre_receive_error.presence
- raise Gitlab::Git::HooksService::PreReceiveError, response.pre_receive_error
+ raise Gitlab::Git::PreReceiveError, response.pre_receive_error
elsif response.git_error.presence
raise Gitlab::Git::Repository::GitError, response.git_error
else
@@ -242,7 +242,7 @@ module Gitlab
:user_commit_files, req_enum, remote_storage: start_repository.storage)
if (pre_receive_error = response.pre_receive_error.presence)
- raise Gitlab::Git::HooksService::PreReceiveError, pre_receive_error
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
end
if (index_error = response.index_error.presence)
@@ -280,7 +280,7 @@ module Gitlab
def handle_cherry_pick_or_revert_response(response)
if response.pre_receive_error.presence
- raise Gitlab::Git::HooksService::PreReceiveError, response.pre_receive_error
+ raise Gitlab::Git::PreReceiveError, response.pre_receive_error
elsif response.commit_error.presence
raise Gitlab::Git::CommitError, response.commit_error
elsif response.create_tree_error.presence
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index ee01f5a5bd9..4340f779e53 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -213,25 +213,20 @@ module Gitlab
end
def create_from_bundle(bundle_path)
- request = Gitaly::CreateRepositoryFromBundleRequest.new(repository: @gitaly_repo)
- enum = Enumerator.new do |y|
- File.open(bundle_path, 'rb') do |f|
- while data = f.read(MAX_MSG_SIZE)
- request.data = data
-
- y.yield request
-
- request = Gitaly::CreateRepositoryFromBundleRequest.new
- end
- end
- end
-
- GitalyClient.call(
- @storage,
- :repository_service,
+ gitaly_repo_stream_request(
+ bundle_path,
:create_repository_from_bundle,
- enum,
- timeout: GitalyClient.default_timeout
+ Gitaly::CreateRepositoryFromBundleRequest,
+ GitalyClient.default_timeout
+ )
+ end
+
+ def restore_custom_hooks(custom_hooks_path)
+ gitaly_repo_stream_request(
+ custom_hooks_path,
+ :restore_custom_hooks,
+ Gitaly::RestoreCustomHooksRequest,
+ GitalyClient.default_timeout
)
end
@@ -311,6 +306,30 @@ module Gitlab
request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query)
GitalyClient.call(@storage, :repository_service, :search_files_by_content, request).flat_map(&:matches)
end
+
+ private
+
+ def gitaly_repo_stream_request(file_path, rpc_name, request_class, timeout)
+ request = request_class.new(repository: @gitaly_repo)
+ enum = Enumerator.new do |y|
+ File.open(file_path, 'rb') do |f|
+ while data = f.read(MAX_MSG_SIZE)
+ request.data = data
+
+ y.yield request
+ request = request_class.new
+ end
+ end
+ end
+
+ GitalyClient.call(
+ @storage,
+ :repository_service,
+ rpc_name,
+ enum,
+ timeout: timeout
+ )
+ end
end
end
end
diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb
index b2f6cb7ad19..6b3688c4381 100644
--- a/lib/gitlab/github_import/importer/pull_request_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_importer.rb
@@ -104,7 +104,8 @@ module Gitlab
# first save the diff, then populate it.
diff =
if already_exists
- merge_request.merge_request_diffs.take
+ merge_request.merge_request_diffs.take ||
+ merge_request.merge_request_diffs.build
else
merge_request.merge_request_diffs.build
end
diff --git a/lib/gitlab/github_import/parallel_importer.rb b/lib/gitlab/github_import/parallel_importer.rb
index b02b123c98e..a77ac1e4fa6 100644
--- a/lib/gitlab/github_import/parallel_importer.rb
+++ b/lib/gitlab/github_import/parallel_importer.rb
@@ -15,6 +15,15 @@ module Gitlab
true
end
+ # This is a workaround for a Ruby 2.3.7 bug. rspec-mocks cannot restore
+ # the visibility of prepended modules. See
+ # https://github.com/rspec/rspec-mocks/issues/1231 for more details.
+ if Rails.env.test?
+ def self.requires_ci_cd_setup?
+ raise NotImplementedError
+ end
+ end
+
def initialize(project)
@project = project
end
diff --git a/lib/gitlab/github_import/sequential_importer.rb b/lib/gitlab/github_import/sequential_importer.rb
index 3cad919b4eb..6a181caf65d 100644
--- a/lib/gitlab/github_import/sequential_importer.rb
+++ b/lib/gitlab/github_import/sequential_importer.rb
@@ -42,8 +42,6 @@ module Gitlab
klass.new(project, client, parallel: false).execute
end
- project.repository.after_import
-
true
end
end
diff --git a/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb b/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb
index 1e1fdabca93..0014ce2689b 100644
--- a/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb
+++ b/lib/gitlab/grape_logging/formatters/lograge_with_timestamp.rb
@@ -2,8 +2,12 @@ module Gitlab
module GrapeLogging
module Formatters
class LogrageWithTimestamp
+ include Gitlab::EncodingHelper
+
def call(severity, datetime, _, data)
time = data.delete :time
+ data[:params] = utf8_encode_values(data[:params]) if data.has_key?(:params)
+
attributes = {
time: datetime.utc.iso8601(3),
severity: severity,
@@ -13,6 +17,19 @@ module Gitlab
}.merge(data)
::Lograge.formatter.call(attributes) + "\n"
end
+
+ private
+
+ def utf8_encode_values(data)
+ case data
+ when Hash
+ data.merge(data) { |k, v| utf8_encode_values(v) }
+ when Array
+ data.map { |v| utf8_encode_values(v) }
+ when String
+ encode_utf8(data)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb
new file mode 100644
index 00000000000..9251ed654cd
--- /dev/null
+++ b/lib/gitlab/hashed_storage/migrator.rb
@@ -0,0 +1,57 @@
+module Gitlab
+ module HashedStorage
+ # Hashed Storage Migrator
+ #
+ # This is responsible for scheduling and flagging projects
+ # to be migrated from Legacy to Hashed storage, either one by one or in bulk.
+ class Migrator
+ BATCH_SIZE = 100
+
+ # Schedule a range of projects to be bulk migrated with #bulk_migrate asynchronously
+ #
+ # @param [Object] start first project id for the range
+ # @param [Object] finish last project id for the range
+ def bulk_schedule(start, finish)
+ StorageMigratorWorker.perform_async(start, finish)
+ end
+
+ # Start migration of projects from specified range
+ #
+ # Flagging a project to be migrated is a synchronous action,
+ # but the migration runs through async jobs
+ #
+ # @param [Object] start first project id for the range
+ # @param [Object] finish last project id for the range
+ def bulk_migrate(start, finish)
+ projects = build_relation(start, finish)
+
+ projects.with_route.find_each(batch_size: BATCH_SIZE) do |project|
+ migrate(project)
+ end
+ end
+
+ # Flag a project to me migrated
+ #
+ # @param [Object] project that will be migrated
+ def migrate(project)
+ Rails.logger.info "Starting storage migration of #{project.full_path} (ID=#{project.id})..."
+
+ project.migrate_to_hashed_storage!
+ rescue => err
+ Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
+ end
+
+ private
+
+ def build_relation(start, finish)
+ relation = Project
+ table = Project.arel_table
+
+ relation = relation.where(table[:id].gteq(start)) if start
+ relation = relation.where(table[:id].lteq(finish)) if finish
+
+ relation
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/hashed_storage/rake_helper.rb b/lib/gitlab/hashed_storage/rake_helper.rb
index 8aba42ccfce..303b05e6a9a 100644
--- a/lib/gitlab/hashed_storage/rake_helper.rb
+++ b/lib/gitlab/hashed_storage/rake_helper.rb
@@ -9,8 +9,20 @@ module Gitlab
ENV.fetch('LIMIT', 500).to_i
end
+ def self.range_from
+ ENV['ID_FROM']
+ end
+
+ def self.range_to
+ ENV['ID_TO']
+ end
+
+ def self.range_single_item?
+ !range_from.nil? && range_from == range_to
+ end
+
def self.project_id_batches(&block)
- Project.with_unmigrated_storage.in_batches(of: batch_size, start: ENV['ID_FROM'], finish: ENV['ID_TO']) do |relation| # rubocop: disable Cop/InBatches
+ Project.with_unmigrated_storage.in_batches(of: batch_size, start: range_from, finish: range_to) do |relation| # rubocop: disable Cop/InBatches
ids = relation.pluck(:id)
yield ids.min, ids.max
diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb
index 4b3e8d0a6a0..38f119cf06d 100644
--- a/lib/gitlab/metrics/subscribers/active_record.rb
+++ b/lib/gitlab/metrics/subscribers/active_record.rb
@@ -20,7 +20,7 @@ module Gitlab
define_histogram :gitlab_sql_duration_seconds do
docstring 'SQL time'
base_labels Transaction::BASE_LABELS
- buckets [0.001, 0.01, 0.1, 1.0, 10.0]
+ buckets [0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
end
def current_transaction
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index f3e48083c19..9f903e96585 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -140,7 +140,7 @@ module Gitlab
define_histogram :gitlab_transaction_duration_seconds do
docstring 'Transaction duration'
base_labels BASE_LABELS
- buckets [0.001, 0.01, 0.1, 1.0, 10.0]
+ buckets [0.1, 0.25, 0.5, 1.0, 2.5, 5.0]
end
define_histogram :gitlab_transaction_allocated_memory_bytes do
diff --git a/lib/gitlab/path_regex.rb b/lib/gitlab/path_regex.rb
index 4dc38aae61e..e5191f5c7f9 100644
--- a/lib/gitlab/path_regex.rb
+++ b/lib/gitlab/path_regex.rb
@@ -30,7 +30,7 @@ module Gitlab
dashboard
deploy.html
explore
- favicon.ico
+ favicon.png
files
groups
health_check
diff --git a/lib/gitlab/quick_actions/extractor.rb b/lib/gitlab/quick_actions/extractor.rb
index 075ff91700c..30c6806b68e 100644
--- a/lib/gitlab/quick_actions/extractor.rb
+++ b/lib/gitlab/quick_actions/extractor.rb
@@ -39,7 +39,7 @@ module Gitlab
content.delete!("\r")
content.gsub!(commands_regex) do
if $~[:cmd]
- commands << [$~[:cmd], $~[:arg]].reject(&:blank?)
+ commands << [$~[:cmd].downcase, $~[:arg]].reject(&:blank?)
''
else
$~[0]
@@ -102,14 +102,14 @@ module Gitlab
# /close
^\/
- (?<cmd>#{Regexp.union(names)})
+ (?<cmd>#{Regexp.new(Regexp.union(names).source, Regexp::IGNORECASE)})
(?:
[ ]
(?<arg>[^\n]*)
)?
(?:\n|$)
)
- }mx
+ }mix
end
def perform_substitutions(content, commands)
@@ -120,7 +120,7 @@ module Gitlab
end
substitution_definitions.each do |substitution|
- match_data = substitution.match(content)
+ match_data = substitution.match(content.downcase)
if match_data
command = [substitution.name.to_s]
command << match_data[1] unless match_data[1].empty?
diff --git a/lib/gitlab/quick_actions/substitution_definition.rb b/lib/gitlab/quick_actions/substitution_definition.rb
index 032c49ed159..688056e5d73 100644
--- a/lib/gitlab/quick_actions/substitution_definition.rb
+++ b/lib/gitlab/quick_actions/substitution_definition.rb
@@ -15,7 +15,7 @@ module Gitlab
return unless content
all_names.each do |a_name|
- content.gsub!(%r{/#{a_name} ?(.*)$}, execute_block(action_block, context, '\1'))
+ content.gsub!(%r{/#{a_name} ?(.*)$}i, execute_block(action_block, context, '\1'))
end
content
end
diff --git a/lib/gitlab/setup_helper.rb b/lib/gitlab/setup_helper.rb
index e5c02dd8ecc..4a87f43597e 100644
--- a/lib/gitlab/setup_helper.rb
+++ b/lib/gitlab/setup_helper.rb
@@ -24,7 +24,9 @@ module Gitlab
address = val['gitaly_address']
end
- storages << { name: key, path: val.legacy_disk_path }
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ storages << { name: key, path: val.legacy_disk_path }
+ end
end
if Rails.env.test?
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 4a691d640b3..4b8aae4f5a2 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -106,10 +106,17 @@ module Gitlab
raise Error.new("don't use disk paths with import_repository: #{url.inspect}")
end
- # The timeout ensures the subprocess won't hang forever
- cmd = gitlab_projects(storage, "#{name}.git")
- success = cmd.import_project(url, git_timeout)
+ relative_path = "#{name}.git"
+ cmd = gitaly_migrate(:import_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
+ if is_enabled
+ GitalyGitlabProjects.new(storage, relative_path)
+ else
+ # The timeout ensures the subprocess won't hang forever
+ gitlab_projects(storage, relative_path)
+ end
+ end
+ success = cmd.import_project(url, git_timeout)
raise Error, cmd.output unless success
success
@@ -165,8 +172,16 @@ module Gitlab
#
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/817
def fork_repository(forked_from_storage, forked_from_disk_path, forked_to_storage, forked_to_disk_path)
- gitlab_projects(forked_from_storage, "#{forked_from_disk_path}.git")
- .fork_repository(forked_to_storage, "#{forked_to_disk_path}.git")
+ forked_from_relative_path = "#{forked_from_disk_path}.git"
+ fork_args = [forked_to_storage, "#{forked_to_disk_path}.git"]
+
+ gitaly_migrate(:fork_repository, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled|
+ if is_enabled
+ GitalyGitlabProjects.new(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
+ else
+ gitlab_projects(forked_from_storage, forked_from_relative_path).fork_repository(*fork_args)
+ end
+ end
end
# Removes a repository from file system, using rm_diretory which is an alias
@@ -452,5 +467,39 @@ module Gitlab
# need to do the same here...
raise Error, e
end
+
+ class GitalyGitlabProjects
+ attr_reader :shard_name, :repository_relative_path, :output
+
+ def initialize(shard_name, repository_relative_path)
+ @shard_name = shard_name
+ @repository_relative_path = repository_relative_path
+ @output = ''
+ end
+
+ def import_project(source, _timeout)
+ raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil)
+
+ Gitlab::GitalyClient::RepositoryService.new(raw_repository).import_repository(source)
+ true
+ rescue GRPC::BadStatus => e
+ @output = e.message
+ false
+ end
+
+ def fork_repository(new_shard_name, new_repository_relative_path)
+ target_repository = Gitlab::Git::Repository.new(new_shard_name, new_repository_relative_path, nil)
+ raw_repository = Gitlab::Git::Repository.new(shard_name, repository_relative_path, nil)
+
+ Gitlab::GitalyClient::RepositoryService.new(target_repository).fork_repository(raw_repository)
+ rescue GRPC::BadStatus => e
+ logger.error "fork-repository failed: #{e.message}"
+ false
+ end
+
+ def logger
+ Rails.logger
+ end
+ end
end
end
diff --git a/lib/gitlab/sql/cte.rb b/lib/gitlab/sql/cte.rb
new file mode 100644
index 00000000000..f357829ba3f
--- /dev/null
+++ b/lib/gitlab/sql/cte.rb
@@ -0,0 +1,50 @@
+module Gitlab
+ module SQL
+ # Class for easily building CTE statements.
+ #
+ # Example:
+ #
+ # cte = CTE.new(:my_cte_name)
+ # ns = Arel::Table.new(:namespaces)
+ #
+ # cte << Namespace.
+ # where(ns[:parent_id].eq(some_namespace_id))
+ #
+ # Namespace
+ # with(cte.to_arel).
+ # from(cte.alias_to(ns))
+ class CTE
+ attr_reader :table, :query
+
+ # name - The name of the CTE as a String or Symbol.
+ def initialize(name, query)
+ @table = Arel::Table.new(name)
+ @query = query
+ end
+
+ # Returns the Arel relation for this CTE.
+ def to_arel
+ sql = Arel::Nodes::SqlLiteral.new("(#{query.to_sql})")
+
+ Arel::Nodes::As.new(table, sql)
+ end
+
+ # Returns an "AS" statement that aliases the CTE name as the given table
+ # name. This allows one to trick ActiveRecord into thinking it's selecting
+ # from an actual table, when in reality it's selecting from a CTE.
+ #
+ # alias_table - The Arel table to use as the alias.
+ def alias_to(alias_table)
+ Arel::Nodes::As.new(table, alias_table)
+ end
+
+ # Applies the CTE to the given relation, returning a new one that will
+ # query from it.
+ def apply_to(relation)
+ relation.except(:where)
+ .with(to_arel)
+ .from(alias_to(relation.model.arel_table))
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/task_helpers.rb b/lib/gitlab/task_helpers.rb
index 723e655c150..922418966e9 100644
--- a/lib/gitlab/task_helpers.rb
+++ b/lib/gitlab/task_helpers.rb
@@ -140,7 +140,9 @@ module Gitlab
end
def repository_storage_paths_args
- Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path }
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.values.map { |rs| rs.legacy_disk_path }
+ end
end
def user_home
diff --git a/lib/gitlab/themes.rb b/lib/gitlab/themes.rb
index d43eff5ba4a..694b01b272c 100644
--- a/lib/gitlab/themes.rb
+++ b/lib/gitlab/themes.rb
@@ -12,11 +12,16 @@ module Gitlab
# All available Themes
THEMES = [
- Theme.new(1, 'Indigo', 'ui_indigo'),
- Theme.new(2, 'Dark', 'ui_dark'),
- Theme.new(3, 'Light', 'ui_light'),
- Theme.new(4, 'Blue', 'ui_blue'),
- Theme.new(5, 'Green', 'ui_green')
+ Theme.new(1, 'Indigo', 'ui-indigo'),
+ Theme.new(6, 'Light Indigo', 'ui-light-indigo'),
+ Theme.new(4, 'Blue', 'ui-blue'),
+ Theme.new(7, 'Light Blue', 'ui-light-blue'),
+ Theme.new(5, 'Green', 'ui-green'),
+ Theme.new(8, 'Light Green', 'ui-light-green'),
+ Theme.new(9, 'Red', 'ui-red'),
+ Theme.new(10, 'Light Red', 'ui-light-red'),
+ Theme.new(2, 'Dark', 'ui-dark'),
+ Theme.new(3, 'Light', 'ui-light')
].freeze
# Convenience method to get a space-separated String of all the theme
diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb
index 20be193ea0c..38be75b7482 100644
--- a/lib/gitlab/url_blocker.rb
+++ b/lib/gitlab/url_blocker.rb
@@ -5,7 +5,7 @@ module Gitlab
BlockedUrlError = Class.new(StandardError)
class << self
- def validate!(url, allow_localhost: false, allow_local_network: true, ports: [], protocols: [])
+ def validate!(url, allow_localhost: false, allow_local_network: true, enforce_user: false, ports: [], protocols: [])
return true if url.nil?
begin
@@ -20,7 +20,7 @@ module Gitlab
port = uri.port || uri.default_port
validate_protocol!(uri.scheme, protocols)
validate_port!(port, ports) if ports.any?
- validate_user!(uri.user)
+ validate_user!(uri.user) if enforce_user
validate_hostname!(uri.hostname)
begin
diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb
index 824e2d7251f..e64033b0dba 100644
--- a/lib/gitlab/url_builder.rb
+++ b/lib/gitlab/url_builder.rb
@@ -26,6 +26,8 @@ module Gitlab
project_snippet_url(object.project, object)
when Snippet
snippet_url(object)
+ when Milestone
+ milestone_url(object)
else
raise NotImplementedError.new("No URL builder defined for #{object.class}")
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index e294f3c4ebc..59a222b086c 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -21,6 +21,7 @@ module Gitlab
uuid: Gitlab::CurrentSettings.uuid,
hostname: Gitlab.config.gitlab.host,
version: Gitlab::VERSION,
+ installation_type: Gitlab::INSTALLATION_TYPE,
active_user_count: User.active.count,
recorded_at: Time.now,
mattermost_enabled: Gitlab.config.mattermost.enabled,
diff --git a/lib/gitlab/verify/batch_verifier.rb b/lib/gitlab/verify/batch_verifier.rb
index 1ef369a4b67..167ba1b3149 100644
--- a/lib/gitlab/verify/batch_verifier.rb
+++ b/lib/gitlab/verify/batch_verifier.rb
@@ -7,13 +7,15 @@ module Gitlab
@batch_size = batch_size
@start = start
@finish = finish
+
+ fix_google_api_logger
end
# Yields a Range of IDs and a Hash of failed verifications (object => error)
def run_batches(&blk)
- relation.in_batches(of: batch_size, start: start, finish: finish) do |relation| # rubocop: disable Cop/InBatches
- range = relation.first.id..relation.last.id
- failures = run_batch(relation)
+ all_relation.in_batches(of: batch_size, start: start, finish: finish) do |batch| # rubocop: disable Cop/InBatches
+ range = batch.first.id..batch.last.id
+ failures = run_batch_for(batch)
yield(range, failures)
end
@@ -29,24 +31,56 @@ module Gitlab
private
- def run_batch(relation)
- relation.map { |upload| verify(upload) }.compact.to_h
+ def run_batch_for(batch)
+ batch.map { |upload| verify(upload) }.compact.to_h
end
def verify(object)
+ local?(object) ? verify_local(object) : verify_remote(object)
+ rescue => err
+ failure(object, err.inspect)
+ end
+
+ def verify_local(object)
expected = expected_checksum(object)
actual = actual_checksum(object)
- raise 'Checksum missing' unless expected.present?
- raise 'Checksum mismatch' unless expected == actual
+ return failure(object, 'Checksum missing') unless expected.present?
+ return failure(object, 'Checksum mismatch') unless expected == actual
+
+ success
+ end
+ # We don't calculate checksum for remote objects, so just check existence
+ def verify_remote(object)
+ return failure(object, 'Remote object does not exist') unless remote_object_exists?(object)
+
+ success
+ end
+
+ def success
nil
- rescue => err
- [object, err]
+ end
+
+ def failure(object, message)
+ [object, message]
+ end
+
+ # It's already set to Logger::INFO, but acts as if it is set to
+ # Logger::DEBUG, and this fixes it...
+ def fix_google_api_logger
+ if Object.const_defined?('Google::Apis')
+ Google::Apis.logger.level = Logger::INFO
+ end
end
# This should return an ActiveRecord::Relation suitable for calling #in_batches on
- def relation
+ def all_relation
+ raise NotImplementedError.new
+ end
+
+ # Should return true if the object is stored locally
+ def local?(_object)
raise NotImplementedError.new
end
@@ -59,6 +93,11 @@ module Gitlab
def actual_checksum(_object)
raise NotImplementedError.new
end
+
+ # Be sure to perform a hard check of the remote object (don't just check DB value)
+ def remote_object_exists?(object)
+ raise NotImplementedError.new
+ end
end
end
end
diff --git a/lib/gitlab/verify/job_artifacts.rb b/lib/gitlab/verify/job_artifacts.rb
index 03500a61074..dbadfbde9e3 100644
--- a/lib/gitlab/verify/job_artifacts.rb
+++ b/lib/gitlab/verify/job_artifacts.rb
@@ -11,10 +11,14 @@ module Gitlab
private
- def relation
+ def all_relation
::Ci::JobArtifact.all
end
+ def local?(artifact)
+ artifact.local_store?
+ end
+
def expected_checksum(artifact)
artifact.file_sha256
end
@@ -22,6 +26,10 @@ module Gitlab
def actual_checksum(artifact)
Digest::SHA256.file(artifact.file.path).hexdigest
end
+
+ def remote_object_exists?(artifact)
+ artifact.file.file.exists?
+ end
end
end
end
diff --git a/lib/gitlab/verify/lfs_objects.rb b/lib/gitlab/verify/lfs_objects.rb
index 970e2a7b718..d3f58a73ac7 100644
--- a/lib/gitlab/verify/lfs_objects.rb
+++ b/lib/gitlab/verify/lfs_objects.rb
@@ -11,8 +11,12 @@ module Gitlab
private
- def relation
- LfsObject.with_files_stored_locally
+ def all_relation
+ LfsObject.all
+ end
+
+ def local?(lfs_object)
+ lfs_object.local_store?
end
def expected_checksum(lfs_object)
@@ -22,6 +26,10 @@ module Gitlab
def actual_checksum(lfs_object)
LfsObject.calculate_oid(lfs_object.file.path)
end
+
+ def remote_object_exists?(lfs_object)
+ lfs_object.file.file.exists?
+ end
end
end
end
diff --git a/lib/gitlab/verify/rake_task.rb b/lib/gitlab/verify/rake_task.rb
index dd138e6b92b..e190eaddc79 100644
--- a/lib/gitlab/verify/rake_task.rb
+++ b/lib/gitlab/verify/rake_task.rb
@@ -45,7 +45,7 @@ module Gitlab
return unless verbose?
failures.each do |object, error|
- say " - #{verifier.describe(object)}: #{error.inspect}".color(:red)
+ say " - #{verifier.describe(object)}: #{error}".color(:red)
end
end
end
diff --git a/lib/gitlab/verify/uploads.rb b/lib/gitlab/verify/uploads.rb
index 0ffa71a6d72..01f09ab8df7 100644
--- a/lib/gitlab/verify/uploads.rb
+++ b/lib/gitlab/verify/uploads.rb
@@ -11,8 +11,12 @@ module Gitlab
private
- def relation
- Upload.with_files_stored_locally
+ def all_relation
+ Upload.all
+ end
+
+ def local?(upload)
+ upload.local?
end
def expected_checksum(upload)
@@ -22,6 +26,10 @@ module Gitlab
def actual_checksum(upload)
Upload.hexdigest(upload.absolute_path)
end
+
+ def remote_object_exists?(upload)
+ upload.build_uploader.file.exists?
+ end
end
end
end
diff --git a/lib/microsoft_teams/notifier.rb b/lib/microsoft_teams/notifier.rb
index c08d3e933a8..226ee1373db 100644
--- a/lib/microsoft_teams/notifier.rb
+++ b/lib/microsoft_teams/notifier.rb
@@ -30,7 +30,7 @@ module MicrosoftTeams
result = { 'sections' => [] }
result['title'] = options[:title]
- result['summary'] = options[:pretext]
+ result['summary'] = options[:summary]
result['sections'] << MicrosoftTeams::Activity.new(options[:activity]).prepare
attachments = options[:attachments]
diff --git a/lib/system_check/orphans/namespace_check.rb b/lib/system_check/orphans/namespace_check.rb
index b5f443abe06..09b57c7b408 100644
--- a/lib/system_check/orphans/namespace_check.rb
+++ b/lib/system_check/orphans/namespace_check.rb
@@ -4,13 +4,15 @@ module SystemCheck
set_name 'Orphaned namespaces:'
def multi_check
- Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
- $stdout.puts
- $stdout.puts "* Storage: #{storage_name} (#{repository_storage.legacy_disk_path})".color(:yellow)
- toplevel_namespace_dirs = disk_namespaces(repository_storage.legacy_disk_path)
-
- orphans = (toplevel_namespace_dirs - existing_namespaces)
- print_orphans(orphans, storage_name)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
+ $stdout.puts
+ $stdout.puts "* Storage: #{storage_name} (#{repository_storage.legacy_disk_path})".color(:yellow)
+ toplevel_namespace_dirs = disk_namespaces(repository_storage.legacy_disk_path)
+
+ orphans = (toplevel_namespace_dirs - existing_namespaces)
+ print_orphans(orphans, storage_name)
+ end
end
clear_namespaces! # releases memory when check finishes
diff --git a/lib/system_check/orphans/repository_check.rb b/lib/system_check/orphans/repository_check.rb
index 5ef0b93ad08..2695c658874 100644
--- a/lib/system_check/orphans/repository_check.rb
+++ b/lib/system_check/orphans/repository_check.rb
@@ -5,16 +5,18 @@ module SystemCheck
attr_accessor :orphans
def multi_check
- Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
- storage_path = repository_storage.legacy_disk_path
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.each do |storage_name, repository_storage|
+ storage_path = repository_storage.legacy_disk_path
- $stdout.puts
- $stdout.puts "* Storage: #{storage_name} (#{storage_path})".color(:yellow)
+ $stdout.puts
+ $stdout.puts "* Storage: #{storage_name} (#{storage_path})".color(:yellow)
- repositories = disk_repositories(storage_path)
- orphans = (repositories - fetch_repositories(storage_name))
+ repositories = disk_repositories(storage_path)
+ orphans = (repositories - fetch_repositories(storage_name))
- print_orphans(orphans, storage_name)
+ print_orphans(orphans, storage_name)
+ end
end
end
diff --git a/lib/system_check/simple_executor.rb b/lib/system_check/simple_executor.rb
index d268f501b4a..99c9e984107 100644
--- a/lib/system_check/simple_executor.rb
+++ b/lib/system_check/simple_executor.rb
@@ -43,7 +43,7 @@ module SystemCheck
#
# @param [SystemCheck::BaseCheck] check_klass
def run_check(check_klass)
- $stdout.print "#{check_klass.display_name} ... "
+ print_display_name(check_klass)
check = check_klass.new
@@ -60,18 +60,18 @@ module SystemCheck
end
if check.check?
- $stdout.puts check_klass.check_pass.color(:green)
+ print_check_pass(check_klass)
else
- $stdout.puts check_klass.check_fail.color(:red)
+ print_check_failure(check_klass)
if check.can_repair?
$stdout.print 'Trying to fix error automatically. ...'
if check.repair!
- $stdout.puts 'Success'.color(:green)
+ print_success
return
else
- $stdout.puts 'Failed'.color(:red)
+ print_failure
end
end
@@ -83,6 +83,26 @@ module SystemCheck
private
+ def print_display_name(check_klass)
+ $stdout.print "#{check_klass.display_name} ... "
+ end
+
+ def print_check_pass(check_klass)
+ $stdout.puts check_klass.check_pass.color(:green)
+ end
+
+ def print_check_failure(check_klass)
+ $stdout.puts check_klass.check_fail.color(:red)
+ end
+
+ def print_success
+ $stdout.puts 'Success'.color(:green)
+ end
+
+ def print_failure
+ $stdout.puts 'Failed'.color(:red)
+ end
+
# Prints header content for the series of checks to be executed for this component
#
# @param [String] component name of the component relative to the checks being executed
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index c04dae7446f..a8acafa9cd9 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -44,11 +44,13 @@ namespace :gitlab do
start_checking "GitLab Shell"
check_gitlab_shell
- check_repo_base_exists
- check_repo_base_is_not_symlink
- check_repo_base_user_and_group
- check_repo_base_permissions
- check_repos_hooks_directory_is_link
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ check_repo_base_exists
+ check_repo_base_is_not_symlink
+ check_repo_base_user_and_group
+ check_repo_base_permissions
+ check_repos_hooks_directory_is_link
+ end
check_gitlab_shell_self_test
finished_checking "GitLab Shell"
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index 289aa5d9060..6de739e9515 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -67,8 +67,10 @@ namespace :gitlab do
puts "GitLab Shell".color(:yellow)
puts "Version:\t#{gitlab_shell_version || "unknown".color(:red)}"
puts "Repository storage paths:"
- Gitlab.config.repositories.storages.each do |name, repository_storage|
- puts "- #{name}: \t#{repository_storage.legacy_disk_path}"
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.each do |name, repository_storage|
+ puts "- #{name}: \t#{repository_storage.legacy_disk_path}"
+ end
end
puts "Hooks:\t\t#{Gitlab.config.gitlab_shell.hooks_path}"
puts "Git:\t\t#{Gitlab.config.git.bin_path}"
diff --git a/lib/tasks/gitlab/storage.rake b/lib/tasks/gitlab/storage.rake
index 68d6f9d7cb1..f539b1df955 100644
--- a/lib/tasks/gitlab/storage.rake
+++ b/lib/tasks/gitlab/storage.rake
@@ -2,9 +2,26 @@ namespace :gitlab do
namespace :storage do
desc 'GitLab | Storage | Migrate existing projects to Hashed Storage'
task migrate_to_hashed: :environment do
- legacy_projects_count = Project.with_unmigrated_storage.count
+ storage_migrator = Gitlab::HashedStorage::Migrator.new
helper = Gitlab::HashedStorage::RakeHelper
+ if helper.range_single_item?
+ project = Project.with_unmigrated_storage.find_by(id: helper.range_from)
+
+ unless project
+ puts "There are no projects requiring storage migration with ID=#{helper.range_from}"
+
+ next
+ end
+
+ puts "Enqueueing storage migration of #{project.full_path} (ID=#{project.id})..."
+ storage_migrator.migrate(project)
+
+ next
+ end
+
+ legacy_projects_count = Project.with_unmigrated_storage.count
+
if legacy_projects_count == 0
puts 'There are no projects requiring storage migration. Nothing to do!'
@@ -14,7 +31,7 @@ namespace :gitlab do
print "Enqueuing migration of #{legacy_projects_count} projects in batches of #{helper.batch_size}"
helper.project_id_batches do |start, finish|
- StorageMigratorWorker.perform_async(start, finish)
+ storage_migrator.bulk_schedule(start, finish)
print '.'
end
diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake
index aafbe52e5f8..fc59b3f937d 100644
--- a/lib/tasks/import.rake
+++ b/lib/tasks/import.rake
@@ -9,7 +9,10 @@ class GithubImport
def initialize(token, gitlab_username, project_path, extras)
@options = { token: token }
@project_path = project_path
- @current_user = User.find_by_username(gitlab_username)
+ @current_user = User.find_by(username: gitlab_username)
+
+ raise "GitLab user #{gitlab_username} not found. Please specify a valid username." unless @current_user
+
@github_repo = extras.empty? ? nil : extras.first
end
@@ -50,7 +53,7 @@ class GithubImport
end
if import_success
- @project.import_finish
+ @project.after_import
puts "Import finished. Timings: #{timings}".color(:green)
else
puts "Import was not successful. Errors were as follows:"
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index e7aab50e42a..f69d204c579 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -22,3 +22,18 @@ task setup_postgresql: :environment do
ProjectNameLowerIndex.new.up
AddPathIndexToRedirectRoutes.new.up
end
+
+desc 'GitLab | Generate PostgreSQL Password Hash'
+task :postgresql_md5_hash do
+ require 'digest'
+ username = ENV.fetch('USERNAME') do |missing|
+ puts "You must provide an username with '#{missing}' ENV variable"
+ exit(1)
+ end
+ password = ENV.fetch('PASSWORD') do |missing|
+ puts "You must provide a password with '#{missing}' ENV variable"
+ exit(1)
+ end
+ hash = Digest::MD5.hexdigest("#{password}#{username}")
+ puts "The MD5 hash of your database password for user: #{username} -> #{hash}"
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index f63f2a89aa9..e8a5a15f410 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-05-29 09:43-0500\n"
-"PO-Revision-Date: 2018-05-29 09:43-0500\n"
+"POT-Creation-Date: 2018-06-12 18:57+1000\n"
+"PO-Revision-Date: 2018-06-12 18:57+1000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
@@ -232,7 +232,7 @@ msgstr ""
msgid "Account"
msgstr ""
-msgid "Account and limit settings"
+msgid "Account and limit"
msgstr ""
msgid "Active"
@@ -262,6 +262,9 @@ msgstr ""
msgid "Add new directory"
msgstr ""
+msgid "Add reaction"
+msgstr ""
+
msgid "Add todo"
msgstr ""
@@ -370,7 +373,7 @@ msgstr ""
msgid "An error occurred while getting projects"
msgstr ""
-msgid "An error occurred while importing project"
+msgid "An error occurred while importing project: ${details}"
msgstr ""
msgid "An error occurred while loading commits"
@@ -463,6 +466,9 @@ msgstr ""
msgid "Assigned to :name"
msgstr ""
+msgid "Assigned to me"
+msgstr ""
+
msgid "Assignee"
msgstr ""
@@ -616,9 +622,6 @@ msgstr ""
msgid "Begin with the selected commit"
msgstr ""
-msgid "Blame"
-msgstr ""
-
msgid "Branch (%{branch_count})"
msgid_plural "Branches (%{branch_count})"
msgstr[0] ""
@@ -780,9 +783,6 @@ msgstr ""
msgid "CI/CD settings"
msgstr ""
-msgid "CICD|A domain is required to use Auto Review Apps and Auto Deploy Stages."
-msgstr ""
-
msgid "CICD|An explicit %{ci_file} needs to be specified before you can begin using Continuous Integration and Delivery."
msgstr ""
@@ -792,6 +792,18 @@ msgstr ""
msgid "CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration."
msgstr ""
+msgid "CICD|Automatic deployment to staging, manual deployment to production"
+msgstr ""
+
+msgid "CICD|Continuous deployment to production"
+msgstr ""
+
+msgid "CICD|Deployment strategy"
+msgstr ""
+
+msgid "CICD|Deployment strategy needs a domain name to work correctly."
+msgstr ""
+
msgid "CICD|Disable Auto DevOps"
msgstr ""
@@ -813,6 +825,9 @@ msgstr ""
msgid "CICD|The Auto DevOps pipeline configuration will be used when there is no %{ci_file} in the project."
msgstr ""
+msgid "CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages."
+msgstr ""
+
msgid "Can run untagged jobs"
msgstr ""
@@ -1041,6 +1056,9 @@ msgstr ""
msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
msgstr ""
+msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
+msgstr ""
+
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
@@ -1086,7 +1104,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|Google Cloud Platform project ID"
+msgid "ClusterIntegration|Google Cloud Platform project"
msgstr ""
msgid "ClusterIntegration|Google Kubernetes Engine"
@@ -1119,6 +1137,12 @@ msgstr ""
msgid "ClusterIntegration|Integration status"
msgstr ""
+msgid "ClusterIntegration|Jupyter Hostname"
+msgstr ""
+
+msgid "ClusterIntegration|JupyterHub"
+msgstr ""
+
msgid "ClusterIntegration|Kubernetes cluster"
msgstr ""
@@ -1519,6 +1543,9 @@ msgstr ""
msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access to the registry images."
msgstr ""
+msgid "Continue"
+msgstr ""
+
msgid "Continuous Integration and Deployment"
msgstr ""
@@ -1558,6 +1585,9 @@ msgstr ""
msgid "Copy reference to clipboard"
msgstr ""
+msgid "Copy to clipboard"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -1627,6 +1657,9 @@ msgstr ""
msgid "Created"
msgstr ""
+msgid "Created by me"
+msgstr ""
+
msgid "Cron Timezone"
msgstr ""
@@ -1818,6 +1851,9 @@ msgstr ""
msgid "DeployTokens|Your new project deploy token has been created."
msgstr ""
+msgid "Deprioritize label"
+msgstr ""
+
msgid "Description"
msgstr ""
@@ -2010,6 +2046,9 @@ msgstr ""
msgid "Error fetching contributors data."
msgstr ""
+msgid "Error fetching job trace"
+msgstr ""
+
msgid "Error fetching labels."
msgstr ""
@@ -2028,6 +2067,9 @@ msgstr ""
msgid "Error loading last commit."
msgstr ""
+msgid "Error loading merge requests."
+msgstr ""
+
msgid "Error loading project data. Please try again."
msgstr ""
@@ -2106,6 +2148,9 @@ msgstr ""
msgid "Failed to update issues, please try again."
msgstr ""
+msgid "Failure"
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -2156,6 +2201,9 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your .gitlab-ci.yml:"
+msgstr ""
+
msgid "From %{provider_title}"
msgstr ""
@@ -2204,6 +2252,9 @@ msgstr ""
msgid "Gitaly"
msgstr ""
+msgid "Gitaly|Address"
+msgstr ""
+
msgid "Gitaly Servers"
msgstr ""
@@ -2341,6 +2392,12 @@ msgstr ""
msgid "Housekeeping successfully started"
msgstr ""
+msgid "I accept the %{terms_link}"
+msgstr ""
+
+msgid "I accept the|Terms of Service and Privacy Policy"
+msgstr ""
+
msgid "IDE|Commit"
msgstr ""
@@ -2350,6 +2407,9 @@ msgstr ""
msgid "IDE|Go back"
msgstr ""
+msgid "IDE|Open in file view"
+msgstr ""
+
msgid "IDE|Review"
msgstr ""
@@ -2374,7 +2434,7 @@ msgstr ""
msgid "Import repository"
msgstr ""
-msgid "Include a Terms of Service agreement that all users must accept."
+msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
msgid "Install Runner on Kubernetes"
@@ -2476,6 +2536,9 @@ msgstr ""
msgid "Label"
msgstr ""
+msgid "Label actions dropdown"
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
@@ -2491,6 +2554,9 @@ msgstr ""
msgid "Labels can be applied to issues and merge requests to categorize them."
msgstr ""
+msgid "Labels can be applied to issues and merge requests."
+msgstr ""
+
msgid "Labels|<span>Promote label</span> %{labelTitle} <span>to Group Label?</span>"
msgstr ""
@@ -2801,7 +2867,10 @@ msgstr ""
msgid "No files found."
msgstr ""
-msgid "No labels created yet."
+msgid "No merge requests found"
+msgstr ""
+
+msgid "No messages were logged"
msgstr ""
msgid "No repository"
@@ -2927,6 +2996,9 @@ msgstr ""
msgid "Only project members can comment."
msgstr ""
+msgid "Open in Xcode"
+msgstr ""
+
msgid "OpenedNDaysAgo|Opened"
msgstr ""
@@ -2939,6 +3011,9 @@ msgstr ""
msgid "Options"
msgstr ""
+msgid "Other Labels"
+msgstr ""
+
msgid "Otherwise it is recommended you start with one of the options below."
msgstr ""
@@ -2984,9 +3059,6 @@ msgstr ""
msgid "Performance optimization"
msgstr ""
-msgid "Permalink"
-msgstr ""
-
msgid "Permissions"
msgstr ""
@@ -3164,6 +3236,21 @@ msgstr ""
msgid "Preferences"
msgstr ""
+msgid "Preferences|Navigation theme"
+msgstr ""
+
+msgid "Prioritize"
+msgstr ""
+
+msgid "Prioritize label"
+msgstr ""
+
+msgid "Prioritized Labels"
+msgstr ""
+
+msgid "Prioritized label"
+msgstr ""
+
msgid "Private - Project access must be granted explicitly to each user."
msgstr ""
@@ -3389,10 +3476,10 @@ msgstr ""
msgid "Promote these project milestones into a group milestone."
msgstr ""
-msgid "Promote to Group Label"
+msgid "Promote to Group Milestone"
msgstr ""
-msgid "Promote to Group Milestone"
+msgid "Promote to group label"
msgstr ""
msgid "Protip:"
@@ -3416,9 +3503,6 @@ msgstr ""
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr ""
-msgid "Raw"
-msgstr ""
-
msgid "Read more"
msgstr ""
@@ -3479,6 +3563,9 @@ msgstr ""
msgid "Remove avatar"
msgstr ""
+msgid "Remove priority"
+msgstr ""
+
msgid "Remove project"
msgstr ""
@@ -3488,7 +3575,7 @@ msgstr ""
msgid "Repository maintenance"
msgstr ""
-msgid "Repository mirror settings"
+msgid "Repository mirror"
msgstr ""
msgid "Repository storage"
@@ -3497,7 +3584,7 @@ msgstr ""
msgid "Request Access"
msgstr ""
-msgid "Require all users to accept Terms of Service when they access GitLab."
+msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
msgid "Reset git storage health information"
@@ -3556,9 +3643,6 @@ msgstr ""
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr ""
-msgid "Runners settings"
-msgstr ""
-
msgid "Running"
msgstr ""
@@ -3586,6 +3670,12 @@ msgstr ""
msgid "Scheduling Pipelines"
msgstr ""
+msgid "Scroll to bottom"
+msgstr ""
+
+msgid "Scroll to top"
+msgstr ""
+
msgid "Search"
msgstr ""
@@ -3601,6 +3691,9 @@ msgstr ""
msgid "Search for projects, issues, etc."
msgstr ""
+msgid "Search merge requests"
+msgstr ""
+
msgid "Search milestones"
msgstr ""
@@ -3709,6 +3802,9 @@ msgstr ""
msgid "Show command"
msgstr ""
+msgid "Show complete raw log"
+msgstr ""
+
msgid "Show parent pages"
msgstr ""
@@ -3744,9 +3840,6 @@ msgstr ""
msgid "Something went wrong when toggling the button"
msgstr ""
-msgid "Something went wrong while fetching the latest pipeline status."
-msgstr ""
-
msgid "Something went wrong while fetching the projects."
msgstr ""
@@ -3870,6 +3963,12 @@ msgstr ""
msgid "Specify the following URL during the Runner setup:"
msgstr ""
+msgid "Squash commits"
+msgstr ""
+
+msgid "Stage"
+msgstr ""
+
msgid "Stage all"
msgstr ""
@@ -3882,6 +3981,9 @@ msgstr ""
msgid "Staged %{type}"
msgstr ""
+msgid "Star a label to make it a priority label. Order the prioritized labels to change their relative priority, by dragging."
+msgstr ""
+
msgid "StarProject|Star"
msgstr ""
@@ -3921,6 +4023,15 @@ msgstr ""
msgid "Subgroups"
msgstr ""
+msgid "Subscribe"
+msgstr ""
+
+msgid "Subscribe at group level"
+msgstr ""
+
+msgid "Subscribe at project level"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
@@ -4001,7 +4112,7 @@ msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
-msgid "TagsPage|Write your release notes or drag files here..."
+msgid "TagsPage|Write your release notes or drag files here…"
msgstr ""
msgid "TagsPage|protected"
@@ -4016,10 +4127,10 @@ msgstr ""
msgid "Team"
msgstr ""
-msgid "Terms of Service"
+msgid "Terms of Service Agreement and Privacy Policy"
msgstr ""
-msgid "Terms of Service Agreement"
+msgid "Terms of Service and Privacy Policy"
msgstr ""
msgid "The Issue Tracker is the place to add things that need to be improved or solved in a project"
@@ -4247,6 +4358,9 @@ msgstr ""
msgid "Timeago|%s days remaining"
msgstr ""
+msgid "Timeago|%s hours ago"
+msgstr ""
+
msgid "Timeago|%s hours remaining"
msgstr ""
@@ -4262,6 +4376,9 @@ msgstr ""
msgid "Timeago|%s months remaining"
msgstr ""
+msgid "Timeago|%s seconds ago"
+msgstr ""
+
msgid "Timeago|%s seconds remaining"
msgstr ""
@@ -4277,46 +4394,43 @@ msgstr ""
msgid "Timeago|%s years remaining"
msgstr ""
-msgid "Timeago|1 day remaining"
-msgstr ""
-
-msgid "Timeago|1 hour remaining"
+msgid "Timeago|1 day ago"
msgstr ""
-msgid "Timeago|1 minute remaining"
+msgid "Timeago|1 day remaining"
msgstr ""
-msgid "Timeago|1 month remaining"
+msgid "Timeago|1 hour ago"
msgstr ""
-msgid "Timeago|1 week remaining"
+msgid "Timeago|1 hour remaining"
msgstr ""
-msgid "Timeago|1 year remaining"
+msgid "Timeago|1 minute ago"
msgstr ""
-msgid "Timeago|Past due"
+msgid "Timeago|1 minute remaining"
msgstr ""
-msgid "Timeago|a day ago"
+msgid "Timeago|1 month ago"
msgstr ""
-msgid "Timeago|a month ago"
+msgid "Timeago|1 month remaining"
msgstr ""
-msgid "Timeago|a week ago"
+msgid "Timeago|1 week ago"
msgstr ""
-msgid "Timeago|a year ago"
+msgid "Timeago|1 week remaining"
msgstr ""
-msgid "Timeago|about %s hours ago"
+msgid "Timeago|1 year ago"
msgstr ""
-msgid "Timeago|about a minute ago"
+msgid "Timeago|1 year remaining"
msgstr ""
-msgid "Timeago|about an hour ago"
+msgid "Timeago|Past due"
msgstr ""
msgid "Timeago|in %s days"
@@ -4358,7 +4472,7 @@ msgstr ""
msgid "Timeago|in 1 year"
msgstr ""
-msgid "Timeago|less than a minute ago"
+msgid "Timeago|just now"
msgstr ""
msgid "Timeago|right now"
@@ -4464,6 +4578,15 @@ msgstr ""
msgid "Unstar"
msgstr ""
+msgid "Unsubscribe"
+msgstr ""
+
+msgid "Unsubscribe at group level"
+msgstr ""
+
+msgid "Unsubscribe at project level"
+msgstr ""
+
msgid "Unverified"
msgstr ""
@@ -4527,9 +4650,15 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View jobs"
+msgstr ""
+
msgid "View labels"
msgstr ""
+msgid "View log"
+msgstr ""
+
msgid "View open merge request"
msgstr ""
@@ -4608,7 +4737,7 @@ msgstr ""
msgid "WikiEmptyIssueMessage|issue tracker"
msgstr ""
-msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, it's principles, how to use it, and so on."
+msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
msgstr ""
msgid "WikiEmpty|Create your first page"
@@ -4680,7 +4809,7 @@ msgstr ""
msgid "WikiPage|Page slug"
msgstr ""
-msgid "WikiPage|Write your content or drag files here..."
+msgid "WikiPage|Write your content or drag files here…"
msgstr ""
msgid "Wiki|Create Page"
@@ -4740,6 +4869,9 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your .gitlab-ci.yml in the %{linkStart}Lint%{linkEnd}"
+msgstr ""
+
msgid "You can easily install a Runner on a Kubernetes cluster. %{link_to_help_page}"
msgstr ""
@@ -4758,12 +4890,21 @@ msgstr ""
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You do not have any assigned merge requests"
+msgstr ""
+
msgid "You have no permissions"
msgstr ""
+msgid "You have not created any merge requests"
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
+msgid "You must accept our Terms of Service and privacy policy in order to register an account"
+msgstr ""
+
msgid "You must have maintainer access to force delete a lock"
msgstr ""
@@ -5093,3 +5234,8 @@ msgstr ""
msgid "with %{additions} additions, %{deletions} deletions."
msgstr ""
+
+msgid "within %d minute "
+msgid_plural "within %d minutes "
+msgstr[0] ""
+msgstr[1] ""
diff --git a/package.json b/package.json
index be3a6e4c9f6..4e1ca905ee8 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,6 @@
"classlist-polyfill": "^1.2.0",
"clipboard": "^1.7.1",
"compression-webpack-plugin": "^1.1.11",
- "copy-webpack-plugin": "^4.5.1",
"core-js": "^2.4.1",
"cropper": "^2.3.0",
"css-loader": "^0.28.11",
@@ -64,7 +63,8 @@
"jszip-utils": "^0.0.2",
"katex": "^0.8.3",
"marked": "^0.3.12",
- "monaco-editor": "0.10.0",
+ "monaco-editor": "0.13.1",
+ "monaco-editor-webpack-plugin": "^1.2.1",
"mousetrap": "^1.4.6",
"pikaday": "^1.6.1",
"popper.js": "^1.14.3",
@@ -94,9 +94,9 @@
"vue-template-compiler": "^2.5.16",
"vue-virtual-scroll-list": "^1.2.5",
"vuex": "^3.0.1",
- "webpack": "^4.7.0",
+ "webpack": "^4.11.1",
"webpack-bundle-analyzer": "^2.11.1",
- "webpack-cli": "^2.1.2",
+ "webpack-cli": "^3.0.2",
"webpack-stats-plugin": "^0.2.1",
"worker-loader": "^2.0.0"
},
@@ -117,7 +117,7 @@
"eslint-plugin-import": "^2.12.0",
"eslint-plugin-jasmine": "^2.1.0",
"eslint-plugin-promise": "^3.8.0",
- "eslint-plugin-vue": "^4.0.1",
+ "eslint-plugin-vue": "^4.5.0",
"ignore": "^3.3.7",
"istanbul": "^0.4.5",
"jasmine-core": "^2.9.0",
diff --git a/public/favicon.ico b/public/favicon.ico
deleted file mode 100644
index 3479cbbb46f..00000000000
--- a/public/favicon.ico
+++ /dev/null
Binary files differ
diff --git a/public/favicon.png b/public/favicon.png
new file mode 100644
index 00000000000..845e0ec34a5
--- /dev/null
+++ b/public/favicon.png
Binary files differ
diff --git a/qa/qa.rb b/qa/qa.rb
index 7f2da05dd63..503379823f4 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -12,7 +12,11 @@ module QA
autoload :Browser, 'qa/runtime/browser'
autoload :Env, 'qa/runtime/env'
autoload :Address, 'qa/runtime/address'
- autoload :API, 'qa/runtime/api'
+
+ module API
+ autoload :Client, 'qa/runtime/api/client'
+ autoload :Request, 'qa/runtime/api/request'
+ end
module Key
autoload :Base, 'qa/runtime/key/base'
diff --git a/qa/qa/factory/repository/push.rb b/qa/qa/factory/repository/push.rb
index 28711c12701..7c0d580c5ca 100644
--- a/qa/qa/factory/repository/push.rb
+++ b/qa/qa/factory/repository/push.rb
@@ -1,9 +1,11 @@
+require 'pathname'
+
module QA
module Factory
module Repository
class Push < Factory::Base
attr_accessor :file_name, :file_content, :commit_message,
- :branch_name, :new_branch
+ :branch_name, :new_branch, :output
attr_writer :remote_branch
@@ -12,6 +14,10 @@ module QA
project.description = 'Project with repository'
end
+ product :output do |factory|
+ factory.output
+ end
+
def initialize
@file_name = 'file.txt'
@file_content = '# This is test project'
@@ -58,7 +64,7 @@ module QA
end
repository.commit(commit_message)
- repository.push_changes("#{branch_name}:#{remote_branch}")
+ @output = repository.push_changes("#{branch_name}:#{remote_branch}")
end
end
end
diff --git a/qa/qa/factory/resource/branch.rb b/qa/qa/factory/resource/branch.rb
index 1785441f5a8..4cabe7eab45 100644
--- a/qa/qa/factory/resource/branch.rb
+++ b/qa/qa/factory/resource/branch.rb
@@ -46,7 +46,9 @@ module QA
resource.remote_branch = @branch_name
end
- Page::Project::Show.act { wait_for_push }
+ Page::Project::Show.perform do |page|
+ page.wait { page.has_content?(branch_name) }
+ end
# The upcoming process will make it access the Protected Branches page,
# select the already created branch and protect it according
@@ -62,13 +64,13 @@ module QA
page.select_branch(branch_name)
if allow_to_push
- page.allow_devs_and_masters_to_push
+ page.allow_devs_and_maintainers_to_push
else
page.allow_no_one_to_push
end
if allow_to_merge
- page.allow_devs_and_masters_to_merge
+ page.allow_devs_and_maintainers_to_merge
else
page.allow_no_one_to_merge
end
@@ -79,9 +81,13 @@ module QA
page.protect_branch
- # Wait for page load, which resets the expanded sections
- page.wait(reload: false) do
- !page.has_content?('Collapse')
+ # Avoid Selenium::WebDriver::Error::StaleElementReferenceError
+ # without sleeping. I.e. this completes fast on fast machines.
+ page.refresh
+
+ # It is possible for the protected branch row to "disappear" at first
+ page.wait do
+ page.has_content?(branch_name)
end
end
end
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 1367671e3ca..fc753554fc4 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -7,8 +7,6 @@ module QA
class Repository
include Scenario::Actable
- attr_reader :push_error
-
def self.perform(*args)
Dir.mktmpdir do |dir|
Dir.chdir(dir) { super }
@@ -35,7 +33,7 @@ module QA
end
def clone(opts = '')
- `git clone #{opts} #{@uri.to_s} ./ #{suppress_output}`
+ run_and_redact_credentials("git clone #{opts} #{@uri} ./")
end
def checkout(branch_name)
@@ -71,8 +69,9 @@ module QA
end
def push_changes(branch = 'master')
- # capture3 returns stdout, stderr and status.
- _, @push_error, _ = Open3.capture3("git push #{@uri} #{branch} #{suppress_output}")
+ output, _ = run_and_redact_credentials("git push #{@uri} #{branch}")
+
+ output
end
def commits
@@ -81,12 +80,10 @@ module QA
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}"
+ # Since the remote URL contains the credentials, and git occasionally
+ # outputs the URL. Note that stderr is redirected to stdout.
+ def run_and_redact_credentials(command)
+ Open3.capture2("#{command} 2>&1 | sed -E 's#://[^@]+@#://****@#g'")
end
end
end
diff --git a/qa/qa/page/admin/settings/main.rb b/qa/qa/page/admin/settings/main.rb
index e7c1220c967..db3387b4557 100644
--- a/qa/qa/page/admin/settings/main.rb
+++ b/qa/qa/page/admin/settings/main.rb
@@ -6,11 +6,11 @@ module QA
include QA::Page::Settings::Common
view 'app/views/admin/application_settings/show.html.haml' do
- element :advanced_settings_section, 'Repository storage'
+ element :repository_storage_settings
end
def expand_repository_storage(&block)
- expand_section('Repository storage') do
+ expand_section(:repository_storage_settings) do
RepositoryStorage.perform(&block)
end
end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 0a69af88570..30e35bf7abb 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -13,7 +13,7 @@ module QA
visit current_url
end
- def wait(max: 60, time: 1, reload: true)
+ def wait(max: 60, time: 0.1, reload: true)
start = Time.now
while Time.now - start < max
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 89125bd2e59..3e0eaa392f5 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -28,11 +28,9 @@ module QA
def has_subgroup?(name)
filter_by_name(name)
- wait(reload: false) do
- break false if page.has_content?('Sorry, no groups or projects matched your search')
+ page.has_text?(/#{name}|Sorry, no groups or projects matched your search/, wait: 60)
- page.has_link?(name)
- end
+ page.has_text?(name, wait: 0)
end
def go_to_new_subgroup
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index f1a859fd8ee..228ffd9d381 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -6,6 +6,7 @@ module QA::Page
view 'app/views/shared/builds/_build_output.html.haml' do
element :build_output, '.js-build-output'
+ element :loading_animation, '.js-build-refresh'
end
view 'app/assets/javascripts/vue_shared/components/ci_badge_link.vue' do
@@ -20,6 +21,10 @@ module QA::Page
find('.ci-status').text == PASSED_STATUS
end
+ def trace_loading?
+ has_css?('.js-build-refresh')
+ end
+
# Reminder: You may wish to wait for a particular job status before checking output
def output
find('.js-build-output').text
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index de849b3eee8..babc0079f3f 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -24,10 +24,10 @@ module QA::Page
end
end
- def has_build?(name, status: :success, wait:)
+ def has_build?(name, status: :success, wait: nil)
within('.pipeline-graph') do
within('.ci-job-component', text: name) do
- has_selector?(".ci-status-icon-#{status}", wait: wait)
+ has_selector?(".ci-status-icon-#{status}", { wait: wait }.compact)
end
end
end
diff --git a/qa/qa/page/project/settings/advanced.rb b/qa/qa/page/project/settings/advanced.rb
index 5ef00504fdf..d7b2b66b587 100644
--- a/qa/qa/page/project/settings/advanced.rb
+++ b/qa/qa/page/project/settings/advanced.rb
@@ -4,9 +4,9 @@ module QA
module Settings
class Advanced < Page::Base
view 'app/views/projects/edit.html.haml' do
- element :project_path_field, 'f.text_field :path'
- element :project_name_field, 'f.text_field :name'
- element :rename_project_button, "f.submit 'Rename project'"
+ element :project_path_field, 'text_field :path'
+ element :project_name_field, 'text_field :name'
+ element :rename_project_button, "submit 'Rename project'"
end
def rename_to(path)
diff --git a/qa/qa/page/project/settings/ci_cd.rb b/qa/qa/page/project/settings/ci_cd.rb
index dfb71e0a9f0..1466bc2e0bf 100644
--- a/qa/qa/page/project/settings/ci_cd.rb
+++ b/qa/qa/page/project/settings/ci_cd.rb
@@ -6,31 +6,33 @@ module QA # rubocop:disable Naming/FileName
include Common
view 'app/views/projects/settings/ci_cd/show.html.haml' do
- element :runners_settings, 'Runners settings'
- element :secret_variables, 'Variables'
- element :auto_devops_section, 'Auto DevOps'
+ element :autodevops_settings
+ element :runners_settings
+ element :variables_settings
end
view 'app/views/projects/settings/ci_cd/_autodevops_form.html.haml' do
- element :enable_auto_devops_button, 'Enable Auto DevOps'
- element :domain_input, 'Domain'
+ element :enable_auto_devops_field, 'radio_button :enabled'
+ element :domain_field, 'text_field :domain'
+ element :enable_auto_devops_button, "%strong= s_('CICD|Enable Auto DevOps')"
+ element :domain_input, "%strong= _('Domain')"
element :save_changes_button, "submit 'Save changes'"
end
def expand_runners_settings(&block)
- expand_section('Runners settings') do
+ expand_section(:runners_settings) do
Settings::Runners.perform(&block)
end
end
def expand_secret_variables(&block)
- expand_section('Variables') do
+ expand_section(:variables_settings) do
Settings::SecretVariables.perform(&block)
end
end
def enable_auto_devops_with_domain(domain)
- expand_section('Auto DevOps') do
+ expand_section(:autodevops_settings) do
choose 'Enable Auto DevOps'
fill_in 'Domain', with: domain
click_on 'Save changes'
diff --git a/qa/qa/page/project/settings/main.rb b/qa/qa/page/project/settings/main.rb
index 5d743f4c9c8..d8cf1d49dd2 100644
--- a/qa/qa/page/project/settings/main.rb
+++ b/qa/qa/page/project/settings/main.rb
@@ -6,11 +6,11 @@ module QA
include Common
view 'app/views/projects/edit.html.haml' do
- element :advanced_settings_section, 'Advanced settings'
+ element :advanced_settings
end
def expand_advanced_settings(&block)
- expand_section('Advanced settings') do
+ expand_section(:advanced_settings) do
Advanced.perform(&block)
end
end
diff --git a/qa/qa/page/project/settings/merge_request.rb b/qa/qa/page/project/settings/merge_request.rb
index a88cd661016..d044d3715a9 100644
--- a/qa/qa/page/project/settings/merge_request.rb
+++ b/qa/qa/page/project/settings/merge_request.rb
@@ -5,17 +5,17 @@ module QA
class MergeRequest < QA::Page::Base
include Common
- view 'app/views/projects/_merge_request_merge_method_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 :merge_request_settings
element :save_merge_request_changes
end
+ view 'app/views/projects/_merge_request_merge_method_settings.html.haml' do
+ element :radio_button_merge_ff
+ end
+
def enable_ff_only
- expand_section('Merge request settings') do
+ expand_section(:merge_request_settings) do
click_element :radio_button_merge_ff
click_element :save_merge_request_changes
end
diff --git a/qa/qa/page/project/settings/protected_branches.rb b/qa/qa/page/project/settings/protected_branches.rb
index a0903c3c4dc..0bd031e96b5 100644
--- a/qa/qa/page/project/settings/protected_branches.rb
+++ b/qa/qa/page/project/settings/protected_branches.rb
@@ -40,7 +40,7 @@ module QA
click_allow(:push, 'No one')
end
- def allow_devs_and_masters_to_push
+ def allow_devs_and_maintainers_to_push
click_allow(:push, 'Developers + Maintainers')
end
@@ -48,7 +48,7 @@ module QA
click_allow(:merge, 'No one')
end
- def allow_devs_and_masters_to_merge
+ def allow_devs_and_maintainers_to_merge
click_allow(:merge, 'Developers + Maintainers')
end
@@ -75,10 +75,6 @@ module QA
within_element(:"allowed_to_#{action}_dropdown") do
click_on text
-
- wait(reload: false) do
- has_css?('.is-active')
- end
end
end
end
diff --git a/qa/qa/page/project/settings/repository.rb b/qa/qa/page/project/settings/repository.rb
index 30900e74e90..1ed5f455a85 100644
--- a/qa/qa/page/project/settings/repository.rb
+++ b/qa/qa/page/project/settings/repository.rb
@@ -6,17 +6,21 @@ module QA
include Common
view 'app/views/projects/deploy_keys/_index.html.haml' do
- element :deploy_keys_section, 'Deploy Keys'
+ element :deploy_keys_settings
+ end
+
+ view 'app/views/projects/protected_branches/shared/_index.html.haml' do
+ element :protected_branches_settings
end
def expand_deploy_keys(&block)
- expand_section('Deploy Keys') do
+ expand_section(:deploy_keys_settings) do
DeployKeys.perform(&block)
end
end
def expand_protected_branches(&block)
- expand_section('Protected Branches') do
+ expand_section(:protected_branches_settings) do
ProtectedBranches.perform(&block)
end
end
diff --git a/qa/qa/page/settings/common.rb b/qa/qa/page/settings/common.rb
index a683a6829d5..f9f71aa4a72 100644
--- a/qa/qa/page/settings/common.rb
+++ b/qa/qa/page/settings/common.rb
@@ -4,19 +4,17 @@ module QA
module Common
# Click the Expand button present in the specified section
#
- # @param [String] name present in the container in the DOM
- def expand_section(name)
- page.within('#content-body') do
- page.within('section', text: name) do
- # Because it is possible to click the button before the JS toggle code is bound
- wait(reload: false) do
- click_button 'Expand' unless first('button', text: 'Collapse')
+ # @param [Symbol] and `element` name defined in a `view` block
+ def expand_section(element_name)
+ within_element(element_name) do
+ # Because it is possible to click the button before the JS toggle code is bound
+ wait(reload: false) do
+ click_button 'Expand' unless first('button', text: 'Collapse')
- page.has_content?('Collapse')
- end
-
- yield if block_given?
+ page.has_content?('Collapse')
end
+
+ yield if block_given?
end
end
end
diff --git a/qa/qa/runtime/api.rb b/qa/qa/runtime/api.rb
deleted file mode 100644
index e2a096b971d..00000000000
--- a/qa/qa/runtime/api.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-require 'airborne'
-
-module QA
- module Runtime
- module API
- class Client
- attr_reader :address
-
- def initialize(address = :gitlab)
- @address = address
- end
-
- def personal_access_token
- @personal_access_token ||= get_personal_access_token
- end
-
- def get_personal_access_token
- # you can set the environment variable PERSONAL_ACCESS_TOKEN
- # to use a specific access token rather than create one from the UI
- if Runtime::Env.personal_access_token
- Runtime::Env.personal_access_token
- else
- create_personal_access_token
- end
- end
-
- private
-
- def create_personal_access_token
- Runtime::Browser.visit(@address, Page::Main::Login) do
- Page::Main::Login.act { sign_in_using_credentials }
- Factory::Resource::PersonalAccessToken.fabricate!.access_token
- end
- end
- end
-
- class Request
- API_VERSION = 'v4'.freeze
-
- def initialize(api_client, path, personal_access_token: nil)
- personal_access_token ||= api_client.personal_access_token
- request_path = request_path(path, personal_access_token: personal_access_token)
- @session_address = Runtime::Address.new(api_client.address, request_path)
- end
-
- def url
- @session_address.address
- end
-
- # Prepend a request path with the path to the API
- #
- # path - Path to append
- #
- # Examples
- #
- # >> request_path('/issues')
- # => "/api/v4/issues"
- #
- # >> request_path('/issues', personal_access_token: 'sometoken)
- # => "/api/v4/issues?private_token=..."
- #
- # Returns the relative path to the requested API resource
- def request_path(path, version: API_VERSION, personal_access_token: nil, oauth_access_token: nil)
- full_path = File.join('/api', version, path)
-
- if oauth_access_token
- query_string = "access_token=#{oauth_access_token}"
- elsif personal_access_token
- query_string = "private_token=#{personal_access_token}"
- end
-
- if query_string
- full_path << (path.include?('?') ? '&' : '?')
- full_path << query_string
- end
-
- full_path
- end
- end
- end
- end
-end
diff --git a/qa/qa/runtime/api/client.rb b/qa/qa/runtime/api/client.rb
new file mode 100644
index 00000000000..02015e23ad8
--- /dev/null
+++ b/qa/qa/runtime/api/client.rb
@@ -0,0 +1,39 @@
+require 'airborne'
+
+module QA
+ module Runtime
+ module API
+ class Client
+ attr_reader :address
+
+ def initialize(address = :gitlab, personal_access_token: nil)
+ @address = address
+ @personal_access_token = personal_access_token
+ end
+
+ def personal_access_token
+ @personal_access_token ||= get_personal_access_token
+ end
+
+ def get_personal_access_token
+ # you can set the environment variable PERSONAL_ACCESS_TOKEN
+ # to use a specific access token rather than create one from the UI
+ if Runtime::Env.personal_access_token
+ Runtime::Env.personal_access_token
+ else
+ create_personal_access_token
+ end
+ end
+
+ private
+
+ def create_personal_access_token
+ Runtime::Browser.visit(@address, Page::Main::Login) do
+ Page::Main::Login.act { sign_in_using_credentials }
+ Factory::Resource::PersonalAccessToken.fabricate!.access_token
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/api/request.rb b/qa/qa/runtime/api/request.rb
new file mode 100644
index 00000000000..c33ada0de7a
--- /dev/null
+++ b/qa/qa/runtime/api/request.rb
@@ -0,0 +1,43 @@
+module QA
+ module Runtime
+ module API
+ class Request
+ API_VERSION = 'v4'.freeze
+
+ def initialize(api_client, path, **query_string)
+ query_string[:private_token] ||= api_client.personal_access_token unless query_string[:oauth_access_token]
+ request_path = request_path(path, **query_string)
+ @session_address = Runtime::Address.new(api_client.address, request_path)
+ end
+
+ def url
+ @session_address.address
+ end
+
+ # Prepend a request path with the path to the API
+ #
+ # path - Path to append
+ #
+ # Examples
+ #
+ # >> request_path('/issues')
+ # => "/api/v4/issues"
+ #
+ # >> request_path('/issues', private_token: 'sometoken)
+ # => "/api/v4/issues?private_token=..."
+ #
+ # Returns the relative path to the requested API resource
+ def request_path(path, version: API_VERSION, **query_string)
+ full_path = File.join('/api', version, path)
+
+ if query_string.any?
+ full_path << (path.include?('?') ? '&' : '?')
+ full_path << query_string.map { |k, v| "#{k}=#{CGI.escape(v)}" }.join('&')
+ end
+
+ full_path
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/browser.rb b/qa/qa/runtime/browser.rb
index a12d95683af..ecd273c6db8 100644
--- a/qa/qa/runtime/browser.rb
+++ b/qa/qa/runtime/browser.rb
@@ -102,19 +102,7 @@ module QA
def perform(&block)
visit(url)
- yield if block_given?
- rescue
- raise if block.nil?
-
- # RSpec examples will take care of screenshots on their own
- #
- unless block.binding.receiver.is_a?(RSpec::Core::ExampleGroup)
- screenshot_and_save_page
- end
-
- raise
- ensure
- clear! if block_given?
+ yield.tap { clear! } if block_given?
end
##
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index fe432edfa2a..81d00d45753 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -46,6 +46,18 @@ module QA
def sandbox_name
ENV['GITLAB_SANDBOX_NAME']
end
+
+ def gcloud_account_key
+ ENV.fetch("GCLOUD_ACCOUNT_KEY")
+ end
+
+ def gcloud_account_email
+ ENV.fetch("GCLOUD_ACCOUNT_EMAIL")
+ end
+
+ def gcloud_zone
+ ENV.fetch('GCLOUD_ZONE')
+ end
end
end
end
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index 604bc522983..7627c8c7ad9 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -20,9 +20,11 @@ module QA
gcloud container clusters
create #{cluster_name}
--enable-legacy-authorization
- --zone us-central1-a
+ --zone #{Runtime::Env.gcloud_zone}
&& gcloud container clusters
- get-credentials #{cluster_name}
+ get-credentials
+ --zone #{Runtime::Env.gcloud_zone}
+ #{cluster_name}
CMD
@api_url = `kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}'`
@@ -32,7 +34,12 @@ module QA
end
def remove!
- shell("gcloud container clusters delete #{cluster_name} --quiet --async")
+ shell <<~CMD.tr("\n", ' ')
+ gcloud container clusters delete
+ --zone #{Runtime::Env.gcloud_zone}
+ #{cluster_name}
+ --quiet --async
+ CMD
end
private
@@ -54,9 +61,9 @@ module QA
def attempt_login_with_env_vars
puts "No gcloud account. Attempting to login from env vars GCLOUD_ACCOUNT_EMAIL and GCLOUD_ACCOUNT_KEY."
gcloud_account_key = Tempfile.new('gcloud-account-key')
- gcloud_account_key.write(ENV.fetch("GCLOUD_ACCOUNT_KEY"))
+ gcloud_account_key.write(Runtime::Env.gcloud_account_key)
gcloud_account_key.close
- gcloud_account_email = ENV.fetch("GCLOUD_ACCOUNT_EMAIL")
+ gcloud_account_email = Runtime::Env.gcloud_account_email
shell("gcloud auth activate-service-account #{gcloud_account_email} --key-file #{gcloud_account_key.path}")
ensure
gcloud_account_key && gcloud_account_key.unlink
diff --git a/qa/qa/specs/features/api/basics_spec.rb b/qa/qa/specs/features/api/basics_spec.rb
new file mode 100644
index 00000000000..1d7f9d6a03c
--- /dev/null
+++ b/qa/qa/specs/features/api/basics_spec.rb
@@ -0,0 +1,61 @@
+require 'securerandom'
+
+module QA
+ feature 'API basics', :core do
+ before(:context) do
+ @api_client = Runtime::API::Client.new(:gitlab)
+ end
+
+ let(:project_name) { "api-basics-#{SecureRandom.hex(8)}" }
+ let(:sanitized_project_path) { CGI.escape("#{Runtime::User.name}/#{project_name}") }
+
+ scenario 'user creates a project with a file and deletes them afterwards' do
+ create_project_request = Runtime::API::Request.new(@api_client, '/projects')
+ post create_project_request.url, path: project_name, name: project_name
+
+ expect_status(201)
+ expect(json_body).to match(
+ a_hash_including(name: project_name, path: project_name)
+ )
+
+ create_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md")
+ post create_file_request.url, branch: 'master', content: 'Hello world', commit_message: 'Add README.md'
+
+ expect_status(201)
+ expect(json_body).to match(
+ a_hash_including(branch: 'master', file_path: 'README.md')
+ )
+
+ get_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", ref: 'master')
+ get get_file_request.url
+
+ expect_status(200)
+ expect(json_body).to match(
+ a_hash_including(
+ ref: 'master',
+ file_path: 'README.md', file_name: 'README.md',
+ encoding: 'base64', content: 'SGVsbG8gd29ybGQ='
+ )
+ )
+
+ delete_file_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/files/README.md", branch: 'master', commit_message: 'Remove README.md')
+ delete delete_file_request.url
+
+ expect_status(204)
+
+ get_tree_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}/repository/tree")
+ get get_tree_request.url
+
+ expect_status(200)
+ expect(json_body).to eq([])
+
+ delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
+ delete delete_project_request.url
+
+ expect_status(202)
+ expect(json_body).to match(
+ a_hash_including(message: '202 Accepted')
+ )
+ end
+ end
+end
diff --git a/qa/qa/specs/features/api/users_spec.rb b/qa/qa/specs/features/api/users_spec.rb
index 38f4c497183..0aecf89e1b7 100644
--- a/qa/qa/specs/features/api/users_spec.rb
+++ b/qa/qa/specs/features/api/users_spec.rb
@@ -31,7 +31,7 @@ module QA
end
scenario 'submit request with an invalid token' do
- request = Runtime::API::Request.new(@api_client, '/users', personal_access_token: 'invalid')
+ request = Runtime::API::Request.new(@api_client, '/users', private_token: 'invalid')
get request.url
diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb
index 0931e649e24..befbc0b281a 100644
--- a/qa/qa/specs/features/merge_request/create_spec.rb
+++ b/qa/qa/specs/features/merge_request/create_spec.rb
@@ -11,7 +11,7 @@ module QA
expect(page).to have_content('This is a merge request')
expect(page).to have_content('Great feature')
- expect(page).to have_content(/Opened [\w\s]+ a minute ago/)
+ expect(page).to have_content(/Opened [\w\s]+ ago/)
end
end
end
diff --git a/qa/qa/specs/features/project/auto_devops_spec.rb b/qa/qa/specs/features/project/auto_devops_spec.rb
index f3f59d33457..202a847d1a5 100644
--- a/qa/qa/specs/features/project/auto_devops_spec.rb
+++ b/qa/qa/specs/features/project/auto_devops_spec.rb
@@ -1,3 +1,5 @@
+require 'pathname'
+
module QA
feature 'Auto Devops', :kubernetes do
after do
diff --git a/qa/qa/specs/features/project/deploy_key_clone_spec.rb b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
index 442ac312b4d..46b3e38c1c5 100644
--- a/qa/qa/specs/features/project/deploy_key_clone_spec.rb
+++ b/qa/qa/specs/features/project/deploy_key_clone_spec.rb
@@ -92,7 +92,9 @@ module QA
Page::Project::Pipeline::Show.act { go_to_first_job }
Page::Project::Job::Show.perform do |job|
- job.wait(reload: false) { job.completed? }
+ job.wait(reload: false) do
+ job.completed? && !job.trace_loading?
+ end
expect(job.passed?).to be_truthy, "Job status did not become \"passed\"."
expect(job.output).to include(sha1sum)
diff --git a/qa/qa/specs/features/repository/protected_branches_spec.rb b/qa/qa/specs/features/repository/protected_branches_spec.rb
index 9e438aa3c30..491675875b9 100644
--- a/qa/qa/specs/features/repository/protected_branches_spec.rb
+++ b/qa/qa/specs/features/repository/protected_branches_spec.rb
@@ -7,12 +7,6 @@ module QA
resource.name = 'protected-branch-project'
end
end
- given(:location) do
- Page::Project::Show.act do
- choose_repository_clone_http
- repository_location
- end
- end
before do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
@@ -26,44 +20,49 @@ module QA
Capybara.execute_script 'localStorage.clear()'
end
- scenario 'user is able to protect a branch' do
- protected_branch = Factory::Resource::Branch.fabricate! do |resource|
- resource.branch_name = branch_name
- resource.project = project
- resource.allow_to_push = true
- resource.protected = true
+ context 'when developers and maintainers are allowed to push to a protected branch' do
+ let!(:protected_branch) { create_protected_branch(allow_to_push: true) }
+
+ scenario 'user with push rights successfully pushes to the protected branch' do
+ expect(protected_branch.name).to have_content(branch_name)
+ expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
+
+ push = push_new_file(branch_name)
+
+ expect(push.output).to match(/remote: To create a merge request for protected-branch, visit/)
end
+ end
+
+ context 'when developers and maintainers are not allowed to push to a protected branch' do
+ scenario 'user without push rights fails to push to the protected branch' do
+ create_protected_branch(allow_to_push: false)
+
+ push = push_new_file(branch_name)
- expect(protected_branch.name).to have_content(branch_name)
- expect(protected_branch.push_allowance).to have_content('Developers + Maintainers')
+ expect(push.output)
+ .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
+ expect(push.output)
+ .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
+ end
end
- scenario 'users without authorization cannot push to protected branch' do
+ def create_protected_branch(allow_to_push:)
Factory::Resource::Branch.fabricate! do |resource|
resource.branch_name = branch_name
resource.project = project
- resource.allow_to_push = false
+ resource.allow_to_push = allow_to_push
resource.protected = true
end
+ end
- project.visit!
-
- Git::Repository.perform do |repository|
- repository.uri = location.uri
- repository.use_default_credentials
-
- repository.act do
- clone
- configure_identity('GitLab QA', 'root@gitlab.com')
- checkout('protected-branch')
- commit_file('README.md', 'readme content', 'Add a readme')
- push_changes('protected-branch')
- end
-
- expect(repository.push_error)
- .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
- expect(repository.push_error)
- .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
+ def push_new_file(branch)
+ Factory::Repository::Push.fabricate! do |resource|
+ resource.project = project
+ resource.file_name = 'new_file.md'
+ resource.file_content = '# This is a new file'
+ resource.commit_message = 'Add new_file.md'
+ resource.branch_name = branch_name
+ resource.new_branch = false
end
end
end
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
new file mode 100644
index 00000000000..ee1f08da238
--- /dev/null
+++ b/qa/spec/git/repository_spec.rb
@@ -0,0 +1,40 @@
+describe QA::Git::Repository do
+ let(:repository) { described_class.new }
+
+ before do
+ cd_empty_temp_directory
+ set_bad_uri
+ repository.use_default_credentials
+ end
+
+ describe '#clone' do
+ it 'redacts credentials from the URI in output' do
+ output, _ = repository.clone
+
+ expect(output).to include("fatal: unable to access 'http://****@foo/bar.git/'")
+ end
+ end
+
+ describe '#push_changes' do
+ before do
+ `git init` # need a repo to push from
+ end
+
+ it 'redacts credentials from the URI in output' do
+ output, _ = repository.push_changes
+
+ expect(output).to include("error: failed to push some refs to 'http://****@foo/bar.git'")
+ end
+ end
+
+ def cd_empty_temp_directory
+ tmp_dir = 'tmp/git-repository-spec/'
+ FileUtils.rm_r(tmp_dir) if File.exist?(tmp_dir)
+ FileUtils.mkdir_p tmp_dir
+ FileUtils.cd tmp_dir
+ end
+
+ def set_bad_uri
+ repository.uri = 'http://foo/bar.git'
+ end
+end
diff --git a/qa/spec/runtime/api_client_spec.rb b/qa/spec/runtime/api/client_spec.rb
index d497d8839b8..d497d8839b8 100644
--- a/qa/spec/runtime/api_client_spec.rb
+++ b/qa/spec/runtime/api/client_spec.rb
diff --git a/qa/spec/runtime/api/request_spec.rb b/qa/spec/runtime/api/request_spec.rb
new file mode 100644
index 00000000000..80e3149f32d
--- /dev/null
+++ b/qa/spec/runtime/api/request_spec.rb
@@ -0,0 +1,44 @@
+describe QA::Runtime::API::Request do
+ include Support::StubENV
+
+ before do
+ stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
+ end
+
+ let(:client) { QA::Runtime::API::Client.new('http://example.com') }
+ let(:request) { described_class.new(client, '/users') }
+
+ describe '#url' do
+ it 'returns the full api request url' do
+ expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token'
+ end
+ end
+
+ describe '#request_path' do
+ it 'prepends the api path' do
+ expect(request.request_path('/users')).to eq '/api/v4/users'
+ end
+
+ it 'adds the personal access token' do
+ expect(request.request_path('/users', private_token: 'token'))
+ .to eq '/api/v4/users?private_token=token'
+ end
+
+ it 'adds the oauth access token' do
+ expect(request.request_path('/users', access_token: 'otoken'))
+ .to eq '/api/v4/users?access_token=otoken'
+ end
+
+ it 'respects query parameters' do
+ expect(request.request_path('/users?page=1')).to eq '/api/v4/users?page=1'
+ expect(request.request_path('/users', private_token: 'token', foo: 'bar/baz'))
+ .to eq '/api/v4/users?private_token=token&foo=bar%2Fbaz'
+ expect(request.request_path('/users?page=1', private_token: 'token', foo: 'bar/baz'))
+ .to eq '/api/v4/users?page=1&private_token=token&foo=bar%2Fbaz'
+ end
+
+ it 'uses a different api version' do
+ expect(request.request_path('/users', version: 'other_version')).to eq '/api/other_version/users'
+ end
+ end
+end
diff --git a/qa/spec/runtime/api_request_spec.rb b/qa/spec/runtime/api_request_spec.rb
index 8cf4b040c24..e69de29bb2d 100644
--- a/qa/spec/runtime/api_request_spec.rb
+++ b/qa/spec/runtime/api_request_spec.rb
@@ -1,42 +0,0 @@
-describe QA::Runtime::API::Request do
- include Support::StubENV
-
- before do
- stub_env('PERSONAL_ACCESS_TOKEN', 'a_token')
- end
-
- let(:client) { QA::Runtime::API::Client.new('http://example.com') }
- let(:request) { described_class.new(client, '/users') }
-
- describe '#url' do
- it 'returns the full api request url' do
- expect(request.url).to eq 'http://example.com/api/v4/users?private_token=a_token'
- end
- end
-
- describe '#request_path' do
- it 'prepends the api path' do
- expect(request.request_path('/users')).to eq '/api/v4/users'
- end
-
- it 'adds the personal access token' do
- expect(request.request_path('/users', personal_access_token: 'token'))
- .to eq '/api/v4/users?private_token=token'
- end
-
- it 'adds the oauth access token' do
- expect(request.request_path('/users', oauth_access_token: 'otoken'))
- .to eq '/api/v4/users?access_token=otoken'
- end
-
- it 'respects query parameters' do
- expect(request.request_path('/users?page=1')).to eq '/api/v4/users?page=1'
- expect(request.request_path('/users?page=1', personal_access_token: 'token'))
- .to eq '/api/v4/users?page=1&private_token=token'
- end
-
- it 'uses a different api version' do
- expect(request.request_path('/users', version: 'other_version')).to eq '/api/other_version/users'
- end
- end
-end
diff --git a/qa/spec/support/stub_env.rb b/qa/spec/support/stub_env.rb
index bc8f3a5e22e..044804cd599 100644
--- a/qa/spec/support/stub_env.rb
+++ b/qa/spec/support/stub_env.rb
@@ -32,6 +32,8 @@ module Support
allow(ENV).to receive(:[]).and_call_original
allow(ENV).to receive(:key?).and_call_original
allow(ENV).to receive(:fetch).and_call_original
+ # Prevent secrets from leaking in CI
+ allow(ENV).to receive(:inspect).and_return([])
add_stubbed_value(STUBBED_KEY, true)
end
end
diff --git a/scripts/rails5-gemfile-lock-check b/scripts/rails5-gemfile-lock-check
new file mode 100755
index 00000000000..da6f1b7145e
--- /dev/null
+++ b/scripts/rails5-gemfile-lock-check
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+
+echo -e "=> Checking if Gemfile.rails5.lock is up-to-date...\\n"
+
+cp Gemfile.rails5.lock Gemfile.rails5.lock.orig
+BUNDLE_GEMFILE=Gemfile.rails5 bundle install "$BUNDLE_INSTALL_FLAGS"
+diff -u Gemfile.rails5.lock.orig Gemfile.rails5.lock >/dev/null 2>&1
+
+if [ $? == 1 ]
+then
+ diff -u Gemfile.rails5.lock.orig Gemfile.rails5.lock
+
+ echo -e "\\n✖ ERROR: Gemfile.rails5.lock is not up-to-date!
+ Please run 'BUNDLE_GEMFILE=Gemfile.rails5 bundle install'\\n" >&2
+ exit 1
+fi
+
+echo "✔ Gemfile.rails5.lock is up-to-date"
+exit 0
diff --git a/spec/controllers/admin/application_settings_controller_spec.rb b/spec/controllers/admin/application_settings_controller_spec.rb
index b4fc2aa326f..9d10d725ff3 100644
--- a/spec/controllers/admin/application_settings_controller_spec.rb
+++ b/spec/controllers/admin/application_settings_controller_spec.rb
@@ -73,7 +73,7 @@ describe Admin::ApplicationSettingsController do
end
it 'updates the restricted_visibility_levels when empty array is passed' do
- put :update, application_setting: { restricted_visibility_levels: [] }
+ put :update, application_setting: { restricted_visibility_levels: [""] }
expect(response).to redirect_to(admin_application_settings_path)
expect(ApplicationSetting.current.restricted_visibility_levels).to be_empty
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 683c57c96f8..74f362fd7fc 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -1,3 +1,4 @@
+# coding: utf-8
require 'spec_helper'
describe ApplicationController do
@@ -457,6 +458,8 @@ describe ApplicationController do
end
context 'for sessionless users' do
+ render_views
+
before do
sign_out user
end
@@ -467,6 +470,14 @@ describe ApplicationController do
expect(response).to have_gitlab_http_status(403)
end
+ it 'renders the error message when the format was html' do
+ get :index,
+ private_token: create(:personal_access_token, user: user).token,
+ format: :html
+
+ expect(response.body).to have_content /accept the terms of service/i
+ end
+
it 'renders a 200 when the sessionless user accepted the terms' do
accept_terms(user)
@@ -478,6 +489,63 @@ describe ApplicationController do
end
end
+ describe '#append_info_to_payload' do
+ controller(described_class) do
+ attr_reader :last_payload
+
+ def index
+ render text: 'authenticated'
+ end
+
+ def append_info_to_payload(payload)
+ super
+
+ @last_payload = payload
+ end
+ end
+
+ it 'does not log errors with a 200 response' do
+ get :index
+
+ expect(controller.last_payload.has_key?(:response)).to be_falsey
+ end
+
+ context '422 errors' do
+ it 'logs a response with a string' do
+ response = spy(ActionDispatch::Response, status: 422, body: 'Hello world', content_type: 'application/json', cookies: {})
+ allow(controller).to receive(:response).and_return(response)
+ get :index
+
+ expect(controller.last_payload[:response]).to eq('Hello world')
+ end
+
+ it 'logs a response with an array' do
+ body = ['I want', 'my hat back']
+ response = spy(ActionDispatch::Response, status: 422, body: body, content_type: 'application/json', cookies: {})
+ allow(controller).to receive(:response).and_return(response)
+ get :index
+
+ expect(controller.last_payload[:response]).to eq(body)
+ end
+
+ it 'does not log a string with an empty body' do
+ response = spy(ActionDispatch::Response, status: 422, body: nil, content_type: 'application/json', cookies: {})
+ allow(controller).to receive(:response).and_return(response)
+ get :index
+
+ expect(controller.last_payload.has_key?(:response)).to be_falsey
+ end
+
+ it 'does not log an HTML body' do
+ response = spy(ActionDispatch::Response, status: 422, body: 'This is a test', content_type: 'application/html', cookies: {})
+ allow(controller).to receive(:response).and_return(response)
+ get :index
+
+ expect(controller.last_payload.has_key?(:response)).to be_falsey
+ end
+ end
+ end
+
describe '#access_denied' do
controller(described_class) do
def index
diff --git a/spec/controllers/boards/issues_controller_spec.rb b/spec/controllers/boards/issues_controller_spec.rb
index dcb0faffbd4..e47ff8661a2 100644
--- a/spec/controllers/boards/issues_controller_spec.rb
+++ b/spec/controllers/boards/issues_controller_spec.rb
@@ -18,7 +18,7 @@ describe Boards::IssuesController do
end
describe 'GET index', :request_store do
- let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
+ let(:johndoe) { create(:user, avatar: fixture_file_upload(File.join('spec/fixtures/dk.png'))) }
context 'with invalid board id' do
it 'returns a not found 404 response' do
diff --git a/spec/controllers/boards/lists_controller_spec.rb b/spec/controllers/boards/lists_controller_spec.rb
index 71d45a22d91..57ccbf1d6b5 100644
--- a/spec/controllers/boards/lists_controller_spec.rb
+++ b/spec/controllers/boards/lists_controller_spec.rb
@@ -156,12 +156,18 @@ describe Boards::ListsController do
def move(user:, board:, list:, position:)
sign_in(user)
- patch :update, namespace_id: project.namespace.to_param,
- project_id: project,
- board_id: board.to_param,
- id: list.to_param,
- list: { position: position },
- format: :json
+ params = { namespace_id: project.namespace.to_param,
+ project_id: project,
+ board_id: board.to_param,
+ id: list.to_param,
+ list: { position: position },
+ format: :json }
+
+ if Gitlab.rails5?
+ patch :update, params: params, as: :json
+ else
+ patch :update, params
+ end
end
end
diff --git a/spec/controllers/groups/avatars_controller_spec.rb b/spec/controllers/groups/avatars_controller_spec.rb
index 506aeee7d2a..7feecd0c380 100644
--- a/spec/controllers/groups/avatars_controller_spec.rb
+++ b/spec/controllers/groups/avatars_controller_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Groups::AvatarsController do
let(:user) { create(:user) }
- let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:group) { create(:group, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
before do
group.add_owner(user)
diff --git a/spec/controllers/import/gitlab_projects_controller_spec.rb b/spec/controllers/import/gitlab_projects_controller_spec.rb
index 8759d3c0b97..d624659bce9 100644
--- a/spec/controllers/import/gitlab_projects_controller_spec.rb
+++ b/spec/controllers/import/gitlab_projects_controller_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Import::GitlabProjectsController do
set(:namespace) { create(:namespace) }
set(:user) { namespace.owner }
- let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
before do
sign_in(user)
diff --git a/spec/controllers/import/google_code_controller_spec.rb b/spec/controllers/import/google_code_controller_spec.rb
index 4241db6e771..0763492d88a 100644
--- a/spec/controllers/import/google_code_controller_spec.rb
+++ b/spec/controllers/import/google_code_controller_spec.rb
@@ -4,7 +4,7 @@ describe Import::GoogleCodeController do
include ImportSpecHelper
let(:user) { create(:user) }
- let(:dump_file) { fixture_file_upload(Rails.root + 'spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') }
+ let(:dump_file) { fixture_file_upload('spec/fixtures/GoogleCodeProjectHosting.json', 'application/json') }
before do
sign_in(user)
diff --git a/spec/controllers/profiles/avatars_controller_spec.rb b/spec/controllers/profiles/avatars_controller_spec.rb
index 4fa0462ccdf..909709e1103 100644
--- a/spec/controllers/profiles/avatars_controller_spec.rb
+++ b/spec/controllers/profiles/avatars_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Profiles::AvatarsController do
- let(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png")) }
+ let(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png")) }
before do
sign_in(user)
diff --git a/spec/controllers/projects/avatars_controller_spec.rb b/spec/controllers/projects/avatars_controller_spec.rb
index 6a41c4d23ea..acfa2730d94 100644
--- a/spec/controllers/projects/avatars_controller_spec.rb
+++ b/spec/controllers/projects/avatars_controller_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::AvatarsController do
- let(:project) { create(:project, :repository, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:project) { create(:project, :repository, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
let(:user) { create(:user) }
before do
diff --git a/spec/controllers/projects/jobs_controller_spec.rb b/spec/controllers/projects/jobs_controller_spec.rb
index a08fcea27a5..06c8a432561 100644
--- a/spec/controllers/projects/jobs_controller_spec.rb
+++ b/spec/controllers/projects/jobs_controller_spec.rb
@@ -265,7 +265,7 @@ describe Projects::JobsController, :clean_gitlab_redis_shared_state do
expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label
expect(json_response['icon']).to eq status.icon
- expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico"
+ expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png"
end
end
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index 6e710c9b20b..22858de0475 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -701,7 +701,7 @@ describe Projects::MergeRequestsController do
expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label
expect(json_response['icon']).to eq status.icon
- expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico"
+ expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png"
end
end
diff --git a/spec/controllers/projects/pipelines_controller_spec.rb b/spec/controllers/projects/pipelines_controller_spec.rb
index 92886e93077..9618a8417ec 100644
--- a/spec/controllers/projects/pipelines_controller_spec.rb
+++ b/spec/controllers/projects/pipelines_controller_spec.rb
@@ -253,7 +253,7 @@ describe Projects::PipelinesController do
expect(json_response['text']).to eq status.text
expect(json_response['label']).to eq status.label
expect(json_response['icon']).to eq status.icon
- expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
+ expect(json_response['favicon']).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
end
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 994da3cd159..705b30f0130 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -6,8 +6,8 @@ describe ProjectsController do
let(:project) { create(:project) }
let(:public_project) { create(:project, :public) }
let(:user) { create(:user) }
- let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
- let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
+ let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
describe 'GET new' do
context 'with an authenticated user' do
@@ -296,16 +296,22 @@ describe ProjectsController do
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
+ original_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path
+ end
expect { update_project path: 'renamed_path' }
.to change { project.reload.path }
expect(project.path).to include 'renamed_path'
+ assign_repository_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ assigns(:repository).path
+ end
+
if project.hashed_storage?(:repository)
- expect(assigns(:repository).path).to eq(original_repository_path)
+ expect(assign_repository_path).to eq(original_repository_path)
else
- expect(assigns(:repository).path).to include(project.path)
+ expect(assign_repository_path).to include(project.path)
end
expect(response).to have_gitlab_http_status(302)
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 346944fd5b0..898f3863008 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe RegistrationsController do
+ include TermsHelper
+
describe '#create' do
let(:user_params) { { user: { name: 'new_user', username: 'new_username', email: 'new@user.com', password: 'Any_password' } } }
@@ -67,6 +69,25 @@ describe RegistrationsController do
expect(flash[:notice]).to include 'Welcome! You have signed up successfully.'
end
end
+
+ context 'when terms are enforced' do
+ before do
+ enforce_terms
+ end
+
+ it 'redirects back with a notice when the checkbox was not checked' do
+ post :create, user_params
+
+ expect(flash[:alert]).to match /you must accept our terms/i
+ end
+
+ it 'creates the user with agreement when terms are accepted' do
+ post :create, user_params.merge(terms_opt_in: '1')
+
+ expect(subject.current_user).to be_present
+ expect(subject.current_user.terms_accepted?).to be(true)
+ end
+ end
end
describe '#destroy' do
diff --git a/spec/controllers/uploads_controller_spec.rb b/spec/controllers/uploads_controller_spec.rb
index 376b229ffc9..1df2c954893 100644
--- a/spec/controllers/uploads_controller_spec.rb
+++ b/spec/controllers/uploads_controller_spec.rb
@@ -6,13 +6,13 @@ shared_examples 'content not cached without revalidation' do
end
describe UploadsController do
- let!(:user) { create(:user, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let!(:user) { create(:user, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
describe 'POST create' do
let(:model) { 'personal_snippet' }
let(:snippet) { create(:personal_snippet, :public) }
- let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
- let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
+ let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
context 'when a user does not have permissions to upload a file' do
it "returns 401 when the user is not logged in" do
@@ -136,7 +136,7 @@ describe UploadsController do
context 'for PNG files' do
it 'returns Content-Disposition: inline' do
note = create(:note, :with_attachment, project: project)
- get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png'
+ get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png'
expect(response['Content-Disposition']).to start_with('inline;')
end
@@ -145,7 +145,7 @@ describe UploadsController do
context 'for SVG files' do
it 'returns Content-Disposition: attachment' do
note = create(:note, :with_svg_attachment, project: project)
- get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.svg'
+ get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'unsanitized.svg'
expect(response['Content-Disposition']).to start_with('attachment;')
end
@@ -164,7 +164,7 @@ describe UploadsController do
end
it "redirects to the sign in page" do
- get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
+ get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png"
expect(response).to redirect_to(new_user_session_path)
end
@@ -172,14 +172,14 @@ describe UploadsController do
context "when the user isn't blocked" do
it "responds with status 200" do
- get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
+ get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'image.png'
+ get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png'
response
end
@@ -189,14 +189,14 @@ describe UploadsController do
context "when not signed in" do
it "responds with status 200" do
- get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "image.png"
+ get :show, model: "user", mounted_as: "avatar", id: user.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'image.png'
+ get :show, model: 'user', mounted_as: 'avatar', id: user.id, filename: 'dk.png'
response
end
@@ -205,7 +205,7 @@ describe UploadsController do
end
context "when viewing a project avatar" do
- let!(:project) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let!(:project) { create(:project, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
context "when the project is public" do
before do
@@ -214,14 +214,14 @@ describe UploadsController do
context "when not signed in" do
it "responds with status 200" do
- get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png'
+ get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png'
response
end
@@ -234,14 +234,14 @@ describe UploadsController do
end
it "responds with status 200" do
- get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png'
+ get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png'
response
end
@@ -256,7 +256,7 @@ describe UploadsController do
context "when not signed in" do
it "redirects to the sign in page" do
- get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png"
expect(response).to redirect_to(new_user_session_path)
end
@@ -279,7 +279,7 @@ describe UploadsController do
end
it "redirects to the sign in page" do
- get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png"
expect(response).to redirect_to(new_user_session_path)
end
@@ -287,14 +287,14 @@ describe UploadsController do
context "when the user isn't blocked" do
it "responds with status 200" do
- get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'image.png'
+ get :show, model: 'project', mounted_as: 'avatar', id: project.id, filename: 'dk.png'
response
end
@@ -304,7 +304,7 @@ describe UploadsController do
context "when the user doesn't have access to the project" do
it "responds with status 404" do
- get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "image.png"
+ get :show, model: "project", mounted_as: "avatar", id: project.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(404)
end
@@ -314,19 +314,19 @@ describe UploadsController do
end
context "when viewing a group avatar" do
- let!(:group) { create(:group, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let!(:group) { create(:group, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
context "when the group is public" do
context "when not signed in" do
it "responds with status 200" do
- get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png'
+ get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png'
response
end
@@ -339,14 +339,14 @@ describe UploadsController do
end
it "responds with status 200" do
- get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png'
+ get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png'
response
end
@@ -375,7 +375,7 @@ describe UploadsController do
end
it "redirects to the sign in page" do
- get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png"
expect(response).to redirect_to(new_user_session_path)
end
@@ -383,14 +383,14 @@ describe UploadsController do
context "when the user isn't blocked" do
it "responds with status 200" do
- get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'image.png'
+ get :show, model: 'group', mounted_as: 'avatar', id: group.id, filename: 'dk.png'
response
end
@@ -400,7 +400,7 @@ describe UploadsController do
context "when the user doesn't have access to the project" do
it "responds with status 404" do
- get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "image.png"
+ get :show, model: "group", mounted_as: "avatar", id: group.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(404)
end
@@ -420,14 +420,14 @@ describe UploadsController do
context "when not signed in" do
it "responds with status 200" do
- get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png'
+ get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png'
response
end
@@ -440,14 +440,14 @@ describe UploadsController do
end
it "responds with status 200" do
- get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png'
+ get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png'
response
end
@@ -462,7 +462,7 @@ describe UploadsController do
context "when not signed in" do
it "redirects to the sign in page" do
- get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png"
expect(response).to redirect_to(new_user_session_path)
end
@@ -485,7 +485,7 @@ describe UploadsController do
end
it "redirects to the sign in page" do
- get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png"
expect(response).to redirect_to(new_user_session_path)
end
@@ -493,14 +493,14 @@ describe UploadsController do
context "when the user isn't blocked" do
it "responds with status 200" do
- get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(200)
end
it_behaves_like 'content not cached without revalidation' do
subject do
- get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'image.png'
+ get :show, model: 'note', mounted_as: 'attachment', id: note.id, filename: 'dk.png'
response
end
@@ -510,7 +510,7 @@ describe UploadsController do
context "when the user doesn't have access to the project" do
it "responds with status 404" do
- get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "image.png"
+ get :show, model: "note", mounted_as: "attachment", id: note.id, filename: "dk.png"
expect(response).to have_gitlab_http_status(404)
end
@@ -521,7 +521,7 @@ describe UploadsController do
context 'Appearance' do
context 'when viewing a custom header logo' do
- let!(:appearance) { create :appearance, header_logo: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') }
+ let!(:appearance) { create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') }
context 'when not signed in' do
it 'responds with status 200' do
@@ -541,7 +541,7 @@ describe UploadsController do
end
context 'when viewing a custom logo' do
- let!(:appearance) { create :appearance, logo: fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') }
+ let!(:appearance) { create :appearance, logo: fixture_file_upload('spec/fixtures/dk.png', 'image/png') }
context 'when not signed in' do
it 'responds with status 200' do
@@ -560,5 +560,43 @@ describe UploadsController do
end
end
end
+
+ context 'original filename or a version filename must match' do
+ let!(:appearance) { create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png', 'image/png') }
+
+ context 'has a valid filename on the original file' do
+ it 'successfully returns the file' do
+ get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'dk.png'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Disposition']).to end_with 'filename="dk.png"'
+ end
+ end
+
+ context 'has an invalid filename on the original file' do
+ it 'returns a 404' do
+ get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'bogus.png'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'has a valid filename on the version file' do
+ it 'successfully returns the file' do
+ get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_main_dk.png'
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.header['Content-Disposition']).to end_with 'filename="favicon_main_dk.png"'
+ end
+ end
+
+ context 'has an invalid filename on the version file' do
+ it 'returns a 404' do
+ get :show, model: 'appearance', mounted_as: 'favicon', id: appearance.id, filename: 'favicon_bogusversion_dk.png'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
end
end
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
index eaf3a4ed497..c909471bb55 100644
--- a/spec/factories/lfs_objects.rb
+++ b/spec/factories/lfs_objects.rb
@@ -7,7 +7,7 @@ FactoryBot.define do
end
trait :with_file do
- file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") }
+ file { fixture_file_upload("spec/fixtures/dk.png", "`/png") }
end
# The uniqueness constraint means we can't use the correct OID for all LFS
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 40f3fa7d69b..9fdc3e616a6 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -130,11 +130,11 @@ FactoryBot.define do
end
trait :with_attachment do
- attachment { fixture_file_upload(Rails.root.join( "spec/fixtures/dk.png"), "image/png") }
+ attachment { fixture_file_upload("spec/fixtures/dk.png", "image/png") }
end
trait :with_svg_attachment do
- attachment { fixture_file_upload(Rails.root.join("spec/fixtures/unsanitized.svg"), "image/svg+xml") }
+ attachment { fixture_file_upload("spec/fixtures/unsanitized.svg", "image/svg+xml") }
end
transient do
diff --git a/spec/factories/project_auto_devops.rb b/spec/factories/project_auto_devops.rb
index 5ce1988c76f..b77f702f9e1 100644
--- a/spec/factories/project_auto_devops.rb
+++ b/spec/factories/project_auto_devops.rb
@@ -3,5 +3,14 @@ FactoryBot.define do
project
enabled true
domain "example.com"
+ deploy_strategy :continuous
+
+ trait :manual do
+ deploy_strategy :manual
+ end
+
+ trait :disabled do
+ enabled false
+ end
end
end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index 16e025618a6..f6b05bac0e8 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -151,11 +151,6 @@ FactoryBot.define do
trait :empty_repo do
after(:create) do |project|
raise "Failed to create repository!" unless project.create_repository
-
- # We delete hooks so that gitlab-shell will not try to authenticate with
- # an API that isn't running
- project.gitlab_shell.rm_directory(project.repository_storage,
- File.join("#{project.disk_path}.git", 'hooks'))
end
end
@@ -180,13 +175,6 @@ FactoryBot.define do
trait :wiki_repo do
after(:create) do |project|
raise 'Failed to create wiki repository!' unless project.create_wiki
-
- # We delete hooks so that gitlab-shell will not try to authenticate with
- # an API that isn't running
- project.gitlab_shell.rm_directory(
- project.repository_storage,
- File.join("#{project.wiki.repository.disk_path}.git", "hooks")
- )
end
end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index d91dcf76191..a5e0ac592b9 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -76,6 +76,26 @@ feature 'Admin Appearance' do
expect(page).not_to have_css(header_logo_selector)
end
+ scenario 'Favicon' do
+ sign_in(create(:admin))
+ visit admin_appearances_path
+
+ attach_file(:appearance_favicon, logo_fixture)
+ click_button 'Save'
+
+ expect(page).to have_css('.appearance-light-logo-preview')
+
+ click_link 'Remove favicon'
+
+ expect(page).not_to have_css('.appearance-light-logo-preview')
+
+ # allowed file types
+ attach_file(:appearance_favicon, Rails.root.join('spec', 'fixtures', 'sanitized.svg'))
+ click_button 'Save'
+
+ expect(page).to have_content 'Favicon You are not allowed to upload "svg" files, allowed types: png, ico'
+ end
+
def expect_custom_sign_in_appearance(appearance)
expect(page).to have_content appearance.title
expect(page).to have_content appearance.description
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index dc025d82937..e7aca94db66 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -94,7 +94,7 @@ feature 'Admin updates settings' do
accept_terms(admin)
page.within('.as-terms') do
- check 'Require all users to accept Terms of Service when they access GitLab.'
+ check 'Require all users to accept Terms of Service and Privacy Policy when they access GitLab.'
fill_in 'Terms of Service Agreement', with: 'Be nice!'
click_button 'Save changes'
end
diff --git a/spec/features/admin/admin_uses_repository_checks_spec.rb b/spec/features/admin/admin_uses_repository_checks_spec.rb
index 90cf5a53787..7371a494d36 100644
--- a/spec/features/admin/admin_uses_repository_checks_spec.rb
+++ b/spec/features/admin/admin_uses_repository_checks_spec.rb
@@ -28,7 +28,7 @@ feature 'Admin uses repository checks' do
visit_admin_project_page(project)
page.within('.alert') do
- expect(page.text).to match(/Last repository check \(.* ago\) failed/)
+ expect(page.text).to match(/Last repository check \(just now\) failed/)
end
end
diff --git a/spec/features/boards/boards_spec.rb b/spec/features/boards/boards_spec.rb
index e414345ac23..f6e0dee28c6 100644
--- a/spec/features/boards/boards_spec.rb
+++ b/spec/features/boards/boards_spec.rb
@@ -150,7 +150,7 @@ describe 'Issue Boards', :js do
click_button 'Add list'
wait_for_requests
- find('.dropdown-menu-close').click
+ find('.js-new-board-list').click
page.within(find('.board:nth-child(2)')) do
accept_confirm { find('.board-delete').click }
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 62a2ec55b00..87fa3f60826 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -47,7 +47,7 @@ describe 'Commits' do
context 'commit status is Ci Build' do
let!(:build) { create(:ci_build, pipeline: pipeline) }
- let(:artifacts_file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+ let(:artifacts_file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
context 'when logged as developer' do
before do
diff --git a/spec/features/issues/form_spec.rb b/spec/features/issues/form_spec.rb
index 4625a50b8d9..2cb3ae08b0e 100644
--- a/spec/features/issues/form_spec.rb
+++ b/spec/features/issues/form_spec.rb
@@ -143,6 +143,9 @@ describe 'New/edit issue', :js do
click_link label.title
click_link label2.title
end
+
+ find('.js-issuable-form-dropdown.js-label-select').click
+
page.within '.js-label-select' do
expect(page).to have_content label.title
end
diff --git a/spec/features/issues/user_uses_slash_commands_spec.rb b/spec/features/issues/user_uses_slash_commands_spec.rb
index fd0aa6cf3a3..dacca494755 100644
--- a/spec/features/issues/user_uses_slash_commands_spec.rb
+++ b/spec/features/issues/user_uses_slash_commands_spec.rb
@@ -153,6 +153,42 @@ feature 'Issues > User uses quick actions', :js do
end
end
+ describe 'make issue confidential' do
+ let(:issue) { create(:issue, project: project) }
+ let(:original_issue) { create(:issue, project: project) }
+
+ context 'when the current user can update issues' do
+ it 'does not create a note, and marks the issue as confidential' do
+ add_note("/confidential")
+
+ expect(page).not_to have_content "/confidential"
+ expect(page).to have_content 'Commands applied'
+ expect(page).to have_content "made the issue confidential"
+
+ expect(issue.reload).to be_confidential
+ end
+ end
+
+ context 'when the current user cannot update the issue' do
+ let(:guest) { create(:user) }
+ before do
+ project.add_guest(guest)
+ gitlab_sign_out
+ sign_in(guest)
+ visit project_issue_path(project, issue)
+ end
+
+ it 'does not create a note, and does not mark the issue as confidential' do
+ add_note("/confidential")
+
+ expect(page).not_to have_content 'Commands applied'
+ expect(page).not_to have_content "made the issue confidential"
+
+ expect(issue.reload).not_to be_confidential
+ end
+ end
+ end
+
describe 'move the issue to another project' do
let(:issue) { create(:issue, project: project) }
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index 4d897f09b57..05228e27963 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -502,6 +502,13 @@ describe 'Copy as GFM', :js do
1. Numbered lists
GFM
+ # list item followed by an HR
+ <<-GFM.strip_heredoc,
+ - list item
+
+ -----
+ GFM
+
'# Heading',
'## Heading',
'### Heading',
@@ -515,8 +522,6 @@ describe 'Copy as GFM', :js do
'~~Strikethrough~~',
- '2^2',
-
'-----',
# table
diff --git a/spec/features/markdown/markdown_spec.rb b/spec/features/markdown/markdown_spec.rb
index c86ba8c50a5..cac8a5068ec 100644
--- a/spec/features/markdown/markdown_spec.rb
+++ b/spec/features/markdown/markdown_spec.rb
@@ -44,7 +44,7 @@ describe 'GitLab Markdown', :aggregate_failures do
# Shared behavior that all pipelines should exhibit
shared_examples 'all pipelines' do
- it 'includes Redcarpet extensions' do
+ it 'includes extensions' do
aggregate_failures 'does not parse emphasis inside of words' do
expect(doc.to_html).not_to match('foo<em>bar</em>baz')
end
@@ -72,10 +72,6 @@ describe 'GitLab Markdown', :aggregate_failures do
aggregate_failures 'parses strikethroughs' do
expect(doc).to have_selector(%{del:contains("and this text doesn't")})
end
-
- aggregate_failures 'parses superscript' do
- expect(doc).to have_selector('sup', count: 2)
- end
end
it 'includes SanitizationFilter' do
@@ -123,16 +119,24 @@ describe 'GitLab Markdown', :aggregate_failures do
expect(doc).to have_selector('details summary:contains("collapsible")')
end
- aggregate_failures 'permits style attribute in th elements' do
- expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
- expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
- expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+ aggregate_failures 'permits align attribute in th elements' do
+ expect(doc.at_css('th:contains("Header")')['align']).to eq 'center'
+ expect(doc.at_css('th:contains("Row")')['align']).to eq 'right'
+ expect(doc.at_css('th:contains("Example")')['align']).to eq 'left'
end
- aggregate_failures 'permits style attribute in td elements' do
- expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
- expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
- expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ aggregate_failures 'permits align attribute in td elements' do
+ expect(doc.at_css('td:contains("Foo")')['align']).to eq 'center'
+ expect(doc.at_css('td:contains("Bar")')['align']).to eq 'right'
+ expect(doc.at_css('td:contains("Baz")')['align']).to eq 'left'
+ end
+
+ aggregate_failures 'permits superscript elements' do
+ expect(doc).to have_selector('sup', count: 2)
+ end
+
+ aggregate_failures 'permits subscript elements' do
+ expect(doc).to have_selector('sub', count: 3)
end
aggregate_failures 'removes `rel` attribute from links' do
@@ -320,6 +324,31 @@ describe 'GitLab Markdown', :aggregate_failures do
end
end
+ context 'Redcarpet documents' do
+ before do
+ allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
+ @html = markdown(@feat.raw_markdown)
+ end
+
+ it 'processes certain elements differently' do
+ aggregate_failures 'parses superscript' do
+ expect(doc).to have_selector('sup', count: 3)
+ end
+
+ aggregate_failures 'permits style attribute in th elements' do
+ expect(doc.at_css('th:contains("Header")')['style']).to eq 'text-align: center'
+ expect(doc.at_css('th:contains("Row")')['style']).to eq 'text-align: right'
+ expect(doc.at_css('th:contains("Example")')['style']).to eq 'text-align: left'
+ end
+
+ aggregate_failures 'permits style attribute in td elements' do
+ expect(doc.at_css('td:contains("Foo")')['style']).to eq 'text-align: center'
+ expect(doc.at_css('td:contains("Bar")')['style']).to eq 'text-align: right'
+ expect(doc.at_css('td:contains("Baz")')['style']).to eq 'text-align: left'
+ end
+ end
+ end
+
# Fake a `current_user` helper
def current_user
@feat.user
diff --git a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
index 7c4fd25bb39..25c408516d1 100644
--- a/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_creates_image_diff_notes_spec.rb
@@ -12,7 +12,7 @@ feature 'Merge request > User creates image diff notes', :js do
# Stub helper to return any blob file as image from public app folder.
# This is necessary to run this specs since we don't display repo images in capybara.
allow_any_instance_of(DiffHelper).to receive(:diff_file_blob_raw_url).and_return('/apple-touch-icon.png')
- allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.ico')
+ allow_any_instance_of(DiffHelper).to receive(:diff_file_old_blob_raw_url).and_return('/favicon.png')
end
context 'create commit diff notes' do
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index b54addce993..3bd9f5e2298 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -139,7 +139,7 @@ describe 'Merge request > User posts notes', :js do
page.within("#note_#{note.id}") do
is_expected.to have_css('.note_edited_ago')
expect(find('.note_edited_ago').text)
- .to match(/less than a minute ago/)
+ .to match(/just now/)
end
end
end
diff --git a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
index fd1629746ef..d3104b448e0 100644
--- a/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_request/user_sees_mini_pipeline_graph_spec.rb
@@ -23,8 +23,8 @@ describe 'Merge request < User sees mini pipeline graph', :js do
end
context 'as json' do
- let(:artifacts_file1) { fixture_file_upload(Rails.root.join('spec/fixtures/banana_sample.gif'), 'image/gif') }
- let(:artifacts_file2) { fixture_file_upload(Rails.root.join('spec/fixtures/dk.png'), 'image/png') }
+ let(:artifacts_file1) { fixture_file_upload(File.join('spec/fixtures/banana_sample.gif'), 'image/gif') }
+ let(:artifacts_file2) { fixture_file_upload(File.join('spec/fixtures/dk.png'), 'image/png') }
before do
create(:ci_build, :success, :trace_artifact, pipeline: pipeline, legacy_artifacts_file: artifacts_file1)
diff --git a/spec/features/projects/issues/user_comments_on_issue_spec.rb b/spec/features/projects/issues/user_comments_on_issue_spec.rb
index c45fdc7642f..353f487485d 100644
--- a/spec/features/projects/issues/user_comments_on_issue_spec.rb
+++ b/spec/features/projects/issues/user_comments_on_issue_spec.rb
@@ -31,11 +31,14 @@ describe "User comments on issue", :js do
end
it "adds comment with code block" do
- comment = "```\nCommand [1]: /usr/local/bin/git , see [text](doc/text)\n```"
+ code_block_content = "Command [1]: /usr/local/bin/git , see [text](doc/text)"
+ comment = "```\n#{code_block_content}\n```"
add_note(comment)
- expect(page).to have_content(comment)
+ wait_for_requests
+
+ expect(page.find('pre code').text).to eq code_block_content
end
end
diff --git a/spec/features/projects/jobs/permissions_spec.rb b/spec/features/projects/jobs/permissions_spec.rb
index 31abadf9bd6..e9588daf37d 100644
--- a/spec/features/projects/jobs/permissions_spec.rb
+++ b/spec/features/projects/jobs/permissions_spec.rb
@@ -88,8 +88,7 @@ describe 'Project Jobs Permissions' do
describe 'artifacts page' do
context 'when recent job has artifacts available' do
before do
- artifacts = Rails.root.join('spec/fixtures/ci_build_artifacts.zip')
- archive = fixture_file_upload(artifacts, 'application/zip')
+ archive = fixture_file_upload('spec/fixtures/ci_build_artifacts.zip')
job.update_attributes(legacy_artifacts_file: archive)
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 9d1c4cbad8b..d2aaf60e72c 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -11,7 +11,7 @@ feature 'Jobs', :clean_gitlab_redis_shared_state do
let(:job2) { create(:ci_build) }
let(:artifacts_file) do
- fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
+ fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif')
end
before do
diff --git a/spec/features/projects/labels/subscription_spec.rb b/spec/features/projects/labels/subscription_spec.rb
index 70e8d436dcb..fafd338e448 100644
--- a/spec/features/projects/labels/subscription_spec.rb
+++ b/spec/features/projects/labels/subscription_spec.rb
@@ -36,7 +36,7 @@ feature 'Labels subscription' do
within "#group_label_#{feature.id}" do
expect(page).not_to have_button 'Unsubscribe'
- click_link_on_dropdown('Group level')
+ click_link_on_dropdown('Subscribe at group level')
expect(page).not_to have_selector('.dropdown-group-label')
expect(page).to have_button 'Unsubscribe'
@@ -45,7 +45,7 @@ feature 'Labels subscription' do
expect(page).to have_selector('.dropdown-group-label')
- click_link_on_dropdown('Project level')
+ click_link_on_dropdown('Subscribe at project level')
expect(page).not_to have_selector('.dropdown-group-label')
expect(page).to have_button 'Unsubscribe'
@@ -68,7 +68,7 @@ feature 'Labels subscription' do
find('.dropdown-group-label').click
page.within('.dropdown-group-label') do
- find('a.js-subscribe-button', text: text).click
+ find('.js-subscribe-button', text: text).click
end
end
end
diff --git a/spec/features/projects/labels/update_prioritization_spec.rb b/spec/features/projects/labels/update_prioritization_spec.rb
index ae8b1364ec7..359381c391c 100644
--- a/spec/features/projects/labels/update_prioritization_spec.rb
+++ b/spec/features/projects/labels/update_prioritization_spec.rb
@@ -102,16 +102,16 @@ feature 'Prioritize labels' do
drag_to(selector: '.label-list-item', from_index: 1, to_index: 2)
page.within('.prioritized-labels') do
- expect(first('li')).to have_content('feature')
- expect(page.all('li').last).to have_content('bug')
+ expect(first('.label-list-item')).to have_content('feature')
+ expect(page.all('.label-list-item').last).to have_content('bug')
end
refresh
wait_for_requests
page.within('.prioritized-labels') do
- expect(first('li')).to have_content('feature')
- expect(page.all('li').last).to have_content('bug')
+ expect(first('.label-list-item')).to have_content('feature')
+ expect(page.all('.label-list-item').last).to have_content('bug')
end
end
diff --git a/spec/features/projects/labels/user_removes_labels_spec.rb b/spec/features/projects/labels/user_removes_labels_spec.rb
index f4fda6de465..efa74015c6e 100644
--- a/spec/features/projects/labels/user_removes_labels_spec.rb
+++ b/spec/features/projects/labels/user_removes_labels_spec.rb
@@ -17,8 +17,9 @@ describe "User removes labels" do
end
it "removes label" do
- page.within(".labels") do
+ page.within(".other-labels") do
page.first(".label-list-item") do
+ first('.js-label-options-dropdown').click
first(".remove-row").click
first(:link, "Delete label").click
end
@@ -36,17 +37,16 @@ describe "User removes labels" do
end
it "removes all labels" do
- page.within(".labels") do
- loop do
- li = page.first(".label-list-item")
- break unless li
+ loop do
+ li = page.first(".label-list-item")
+ break unless li
- li.click_link("Delete")
- click_link("Delete label")
- end
-
- expect(page).to have_content("Generate a default set of labels").and have_content("New label")
+ li.find('.js-label-options-dropdown').click
+ li.click_button("Delete")
+ click_link("Delete label")
end
+
+ expect(page).to have_content("Generate a default set of labels").and have_content("New label")
end
end
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index bdd49f731c7..a2899ec0f48 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -314,8 +314,8 @@ feature 'Pages' do
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'))
+ legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')),
+ legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta'))
)
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 35776a5f23b..ecc7cf84138 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -344,6 +344,16 @@ describe 'Pipeline', :js do
it 'shows build failure logs' do
expect(page).to have_content('4 examples, 1 failure')
end
+
+ it 'shows the failure reason' do
+ expect(page).to have_content('There is an unknown failure, please try again')
+ end
+
+ it 'shows retry button for failed build' do
+ page.within(find('.build-failures', match: :first)) do
+ expect(page).to have_link('Retry')
+ end
+ end
end
context 'when missing build logs' do
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index c0b4fa52526..9981bfa4609 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -38,7 +38,7 @@ feature 'Master deletes tag' do
context 'when Gitaly operation_user_delete_tag feature is enabled' do
before do
allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
- .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags')
+ .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
end
scenario 'shows the error message' do
@@ -51,7 +51,7 @@ feature 'Master deletes tag' do
context 'when Gitaly operation_user_delete_tag feature is disabled', :skip_gitaly_mock do
before do
allow_any_instance_of(Gitlab::Git::HooksService).to receive(:execute)
- .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'Do not delete tags')
+ .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
end
scenario 'shows the error message' do
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 2dc3c5e3927..f37d8998045 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -36,7 +36,7 @@ feature 'Task Lists' do
MARKDOWN
end
- let(:nested_tasks_markdown) do
+ let(:nested_tasks_markdown_redcarpet) do
<<-EOT.strip_heredoc
- [ ] Task a
- [x] Task a.1
@@ -49,6 +49,19 @@ feature 'Task Lists' do
EOT
end
+ let(:nested_tasks_markdown) do
+ <<-EOT.strip_heredoc
+ - [ ] Task a
+ - [x] Task a.1
+ - [ ] Task a.2
+ - [ ] Task b
+
+ 1. [ ] Task 1
+ 1. [ ] Task 1.1
+ 1. [x] Task 1.2
+ EOT
+ end
+
before do
Warden.test_mode!
@@ -141,13 +154,11 @@ feature 'Task Lists' do
end
end
- describe 'nested tasks', :js do
- let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
-
+ shared_examples 'shared nested tasks' do
before do
+ allow(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
visit_issue(project, issue)
end
-
it 'renders' do
expect(page).to have_selector('ul.task-list', count: 2)
expect(page).to have_selector('li.task-list-item', count: 7)
@@ -171,6 +182,30 @@ feature 'Task Lists' do
expect(page).to have_content('marked the task Task 1.1 as complete')
end
end
+
+ describe 'nested tasks', :js do
+ context 'with Redcarpet' do
+ let(:issue) { create(:issue, description: nested_tasks_markdown_redcarpet, author: user, project: project) }
+
+ before do
+ allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('Redcarpet')
+ visit_issue(project, issue)
+ end
+
+ it_behaves_like 'shared nested tasks'
+ end
+
+ context 'with CommonMark' do
+ let(:issue) { create(:issue, description: nested_tasks_markdown, author: user, project: project) }
+
+ before do
+ allow_any_instance_of(Banzai::Filter::MarkdownFilter).to receive(:engine).and_return('CommonMark')
+ visit_issue(project, issue)
+ end
+
+ it_behaves_like 'shared nested tasks'
+ end
+ end
end
describe 'for Notes' do
diff --git a/spec/features/users/signup_spec.rb b/spec/features/users/signup_spec.rb
index b5bd5c505f2..b51ca5d130b 100644
--- a/spec/features/users/signup_spec.rb
+++ b/spec/features/users/signup_spec.rb
@@ -140,7 +140,7 @@ describe 'Signup' do
enforce_terms
end
- it 'asks the user to accept terms before going to the dashboard' do
+ it 'requires the user to check the checkbox' do
visit root_path
fill_in 'new_user_name', with: new_user.name
@@ -148,11 +148,24 @@ describe 'Signup' do
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_to_be_on_terms_page
+ click_button 'Register'
+
+ expect(current_path).to eq new_user_session_path
+ expect(page).to have_content(/you must accept our terms of service/i)
+ end
+
+ it 'asks the user to accept terms before going to the 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
+ check :terms_opt_in
- click_button 'Accept terms'
+ click_button "Register"
expect(current_path).to eq dashboard_projects_path
end
diff --git a/spec/features/users/terms_spec.rb b/spec/features/users/terms_spec.rb
index af407c52917..5b2e7605c4d 100644
--- a/spec/features/users/terms_spec.rb
+++ b/spec/features/users/terms_spec.rb
@@ -3,12 +3,10 @@ require 'spec_helper'
describe 'Users > Terms' do
include TermsHelper
- let(:user) { create(:user) }
let!(:term) { create(:term, terms: 'By accepting, you promise to be nice!') }
before do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
- sign_in(user)
end
it 'shows the terms' do
@@ -17,102 +15,119 @@ describe 'Users > Terms' do
expect(page).to have_content('By accepting, you promise to be nice!')
end
- context 'declining the terms' do
- it 'returns the user to the app' do
- visit terms_path
+ it 'does not show buttons to accept, decline or sign out', :aggregate_failures do
+ visit terms_path
+
+ expect(page).not_to have_css('.footer-block')
+ expect(page).not_to have_content('Accept terms')
+ expect(page).not_to have_content('Decline and sign out')
+ expect(page).not_to have_content('Continue')
+ end
- click_button 'Decline and sign out'
+ context 'when signed in' do
+ let(:user) { create(:user) }
- expect(page).not_to have_content(term.terms)
- expect(user.reload.terms_accepted?).to be(false)
+ before do
+ sign_in(user)
end
- end
- context 'accepting the terms' do
- it 'returns the user to the app' do
- visit terms_path
+ context 'declining the terms' do
+ it 'returns the user to the app' do
+ visit terms_path
- click_button 'Accept terms'
+ click_button 'Decline and sign out'
- expect(page).not_to have_content(term.terms)
- expect(user.reload.terms_accepted?).to be(true)
+ expect(page).not_to have_content(term.terms)
+ expect(user.reload.terms_accepted?).to be(false)
+ end
end
- end
- context 'when the user has already accepted the terms' do
- before do
- accept_terms(user)
+ context 'accepting the terms' do
+ it 'returns the user to the app' do
+ visit terms_path
+
+ click_button 'Accept terms'
+
+ expect(page).not_to have_content(term.terms)
+ expect(user.reload.terms_accepted?).to be(true)
+ end
end
- it 'allows the user to continue to the app' do
- visit terms_path
+ context 'when the user has already accepted the terms' do
+ before do
+ accept_terms(user)
+ end
+
+ it 'allows the user to continue to the app' do
+ visit terms_path
- expect(page).to have_content "You have already accepted the Terms of Service as #{user.to_reference}"
+ expect(page).to have_content "You have already accepted the Terms of Service as #{user.to_reference}"
- click_link 'Continue'
+ click_link 'Continue'
- expect(current_path).to eq(root_path)
+ expect(current_path).to eq(root_path)
+ end
end
- end
- context 'terms were enforced while session is active', :js do
- let(:project) { create(:project) }
+ context 'terms were enforced while session is active', :js do
+ let(:project) { create(:project) }
- before do
- project.add_developer(user)
- end
+ before do
+ project.add_developer(user)
+ end
- it 'redirects to terms and back to where the user was going' do
- visit project_path(project)
+ it 'redirects to terms and back to where the user was going' do
+ visit project_path(project)
- enforce_terms
+ enforce_terms
- within('.nav-sidebar') do
- click_link 'Issues'
- end
+ within('.nav-sidebar') do
+ click_link 'Issues'
+ end
- expect_to_be_on_terms_page
+ expect_to_be_on_terms_page
- click_button('Accept terms')
+ click_button('Accept terms')
- expect(current_path).to eq(project_issues_path(project))
- end
+ expect(current_path).to eq(project_issues_path(project))
+ end
- # Disabled until https://gitlab.com/gitlab-org/gitlab-ce/issues/37162 is solved properly
- xit 'redirects back to the page the user was trying to save' do
- visit new_project_issue_path(project)
+ # Disabled until https://gitlab.com/gitlab-org/gitlab-ce/issues/37162 is solved properly
+ xit 'redirects back to the page the user was trying to save' do
+ visit new_project_issue_path(project)
- fill_in :issue_title, with: 'Hello world, a new issue'
- fill_in :issue_description, with: "We don't want to lose what the user typed"
+ fill_in :issue_title, with: 'Hello world, a new issue'
+ fill_in :issue_description, with: "We don't want to lose what the user typed"
- enforce_terms
+ enforce_terms
- click_button 'Submit issue'
+ click_button 'Submit issue'
- expect(current_path).to eq(terms_path)
+ expect(current_path).to eq(terms_path)
- click_button('Accept terms')
+ click_button('Accept terms')
- expect(current_path).to eq(new_project_issue_path(project))
- expect(find_field('issue_title').value).to eq('Hello world, a new issue')
- expect(find_field('issue_description').value).to eq("We don't want to lose what the user typed")
+ expect(current_path).to eq(new_project_issue_path(project))
+ expect(find_field('issue_title').value).to eq('Hello world, a new issue')
+ expect(find_field('issue_description').value).to eq("We don't want to lose what the user typed")
+ end
end
- end
- context 'when the terms are enforced' do
- before do
- enforce_terms
- end
+ context 'when the terms are enforced' do
+ before do
+ enforce_terms
+ end
- context 'signing out', :js do
- it 'allows the user to sign out without a response' do
- visit terms_path
+ context 'signing out', :js do
+ it 'allows the user to sign out without a response' do
+ visit terms_path
- find('.header-user-dropdown-toggle').click
- click_link('Sign out')
+ find('.header-user-dropdown-toggle').click
+ click_link('Sign out')
- expect(page).to have_content('Sign in')
- expect(page).to have_content('Register')
+ expect(page).to have_content('Sign in')
+ expect(page).to have_content('Register')
+ end
end
end
end
diff --git a/spec/finders/group_members_finder_spec.rb b/spec/finders/group_members_finder_spec.rb
index 9f285e28535..63e15b365a4 100644
--- a/spec/finders/group_members_finder_spec.rb
+++ b/spec/finders/group_members_finder_spec.rb
@@ -29,4 +29,16 @@ describe GroupMembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member3, member4])
end
+
+ it 'returns members for descendant groups if requested', :nested_groups do
+ member1 = group.add_master(user2)
+ member2 = group.add_master(user1)
+ nested_group.add_master(user2)
+ member3 = nested_group.add_master(user3)
+ member4 = nested_group.add_master(user4)
+
+ result = described_class.new(group).execute(include_descendants: true)
+
+ expect(result.to_a).to match_array([member1, member2, member3, member4])
+ end
end
diff --git a/spec/finders/members_finder_spec.rb b/spec/finders/members_finder_spec.rb
index 7bb1f45322e..2fc5299b0f4 100644
--- a/spec/finders/members_finder_spec.rb
+++ b/spec/finders/members_finder_spec.rb
@@ -19,4 +19,16 @@ describe MembersFinder, '#execute' do
expect(result.to_a).to match_array([member1, member2, member3])
end
+
+ it 'includes nested group members if asked', :nested_groups do
+ project = create(:project, namespace: group)
+ nested_group.request_access(user1)
+ member1 = group.add_master(user2)
+ member2 = nested_group.add_master(user3)
+ member3 = project.add_master(user4)
+
+ result = described_class.new(project, user2).execute(include_descendants: true)
+
+ expect(result.to_a).to match_array([member1, member2, member3])
+ end
end
diff --git a/spec/fixtures/api/schemas/list.json b/spec/fixtures/api/schemas/list.json
index 05922df6b81..b76ec115293 100644
--- a/spec/fixtures/api/schemas/list.json
+++ b/spec/fixtures/api/schemas/list.json
@@ -37,5 +37,5 @@
"title": { "type": "string" },
"position": { "type": ["integer", "null"] }
},
- "additionalProperties": false
+ "additionalProperties": true
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json b/spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json
new file mode 100644
index 00000000000..3b5dd547e69
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/commit/with_stats.json
@@ -0,0 +1,14 @@
+{
+ "type": "object",
+ "allOf": [
+ { "$ref": "basic.json" },
+ {
+ "required" : [
+ "stats"
+ ],
+ "properties": {
+ "stats": { "$ref": "../commit_stats.json" }
+ }
+ }
+ ]
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json b/spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json
new file mode 100644
index 00000000000..23511123ce4
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/commits_with_stats.json
@@ -0,0 +1,4 @@
+{
+ "type": "array",
+ "items": { "$ref": "commit/with_stats.json" }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/milestones.json b/spec/fixtures/api/schemas/public_api/v4/milestones.json
index c3c42b6ee60..448e97d6c85 100644
--- a/spec/fixtures/api/schemas/public_api/v4/milestones.json
+++ b/spec/fixtures/api/schemas/public_api/v4/milestones.json
@@ -13,7 +13,8 @@
"created_at": { "type": "date" },
"updated_at": { "type": "date" },
"start_date": { "type": "date" },
- "due_date": { "type": "date" }
+ "due_date": { "type": "date" },
+ "web_url": { "type": "string" }
},
"required": [
"id", "iid", "title", "description", "state",
diff --git a/spec/fixtures/api/schemas/public_api/v4/snippets.json b/spec/fixtures/api/schemas/public_api/v4/snippets.json
index e37e9704649..d13d703e063 100644
--- a/spec/fixtures/api/schemas/public_api/v4/snippets.json
+++ b/spec/fixtures/api/schemas/public_api/v4/snippets.json
@@ -8,6 +8,7 @@
"title": { "type": "string" },
"file_name": { "type": ["string", "null"] },
"description": { "type": ["string", "null"] },
+ "visibility": { "type": "string" },
"web_url": { "type": "string" },
"created_at": { "type": "date" },
"updated_at": { "type": "date" },
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index da32a46675f..e5d01c3bd03 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -43,8 +43,14 @@ This text says this, ~~and this text doesn't~~.
### Superscript
-This is my 1^(st) time using superscript in Markdown. Now this is my
-2^(nd).
+This is my 1<sup>(st)</sup> time using superscript in Markdown. Now this is my
+2<sup>(nd)</sup>.
+
+Redcarpet supports this superscript syntax ( x^2 ).
+
+### Subscript
+
+This (C<sub>6</sub>H<sub>12</sub>O<sub>6</sub>) is an example of subscripts in Markdown.
### Next step
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index 2390c1f3e5d..139387e0b24 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -47,9 +47,7 @@ describe EmailsHelper do
describe '#header_logo' do
context 'there is a brand item with a logo' do
it 'returns the brand header logo' do
- appearance = create :appearance, header_logo: fixture_file_upload(
- Rails.root.join('spec/fixtures/dk.png')
- )
+ appearance = create :appearance, header_logo: fixture_file_upload('spec/fixtures/dk.png')
expect(header_logo).to eq(
%{<img style="height: 50px" src="/uploads/-/system/appearance/header_logo/#{appearance.id}/dk.png" alt="Dk" />}
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index b48c252acd3..6c94bd4e504 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -4,9 +4,9 @@ describe GroupsHelper do
include ApplicationHelper
describe 'group_icon' do
- avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
-
it 'returns an url for the avatar' do
+ avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif')
+
group = create(:group)
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
@@ -17,9 +17,9 @@ describe GroupsHelper do
end
describe 'group_icon_url' do
- avatar_file_path = File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif')
-
it 'returns an url for the avatar' do
+ avatar_file_path = File.join('spec', 'fixtures', 'banana_sample.gif')
+
group = create(:group)
group.avatar = fixture_file_upload(avatar_file_path)
group.save!
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index c0dc9293397..1a720aae55c 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -298,7 +298,7 @@ describe MarkupHelper do
it 'preserves code color scheme' do
object = create_object("```ruby\ndef test\n 'hello world'\nend\n```")
- expected = "\n<pre class=\"code highlight js-syntax-highlight ruby\">" \
+ expected = "<pre class=\"code highlight js-syntax-highlight ruby\">" \
"<code><span class=\"line\"><span class=\"k\">def</span> <span class=\"nf\">test</span>...</span>\n" \
"</code></pre>"
diff --git a/spec/helpers/page_layout_helper_spec.rb b/spec/helpers/page_layout_helper_spec.rb
index b77114a8152..cf98eed27f1 100644
--- a/spec/helpers/page_layout_helper_spec.rb
+++ b/spec/helpers/page_layout_helper_spec.rb
@@ -40,23 +40,6 @@ describe PageLayoutHelper do
end
end
- describe 'favicon' do
- it 'defaults to favicon.ico' do
- allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production'))
- expect(helper.favicon).to eq 'favicon.ico'
- end
-
- it 'has blue favicon for development' do
- allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development'))
- expect(helper.favicon).to eq 'favicon-blue.ico'
- end
-
- it 'has yellow favicon for canary' do
- stub_env('CANARY', 'true')
- expect(helper.favicon).to eq 'favicon-yellow.ico'
- end
- end
-
describe 'page_image' do
it 'defaults to the GitLab logo' do
expect(helper.page_image).to match_asset_path 'assets/gitlab_logo.png'
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index c9d2ec8a4ae..363ebc88afd 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -33,7 +33,7 @@ describe PreferencesHelper do
it "returns user's theme's css_class" do
stub_user(theme_id: 3)
- expect(helper.user_application_theme).to eq 'ui_light'
+ expect(helper.user_application_theme).to eq 'ui-light'
end
it 'returns the default when id is invalid' do
@@ -41,7 +41,7 @@ describe PreferencesHelper do
allow(Gitlab.config.gitlab).to receive(:default_theme).and_return(1)
- expect(helper.user_application_theme).to eq 'ui_indigo'
+ expect(helper.user_application_theme).to eq 'ui-indigo'
end
end
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index d372e58f63d..5cf9e9e8f12 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -90,6 +90,10 @@ describe ProjectsHelper do
expect(helper.project_list_cache_key(project)).to include(project.cache_key)
end
+ it "includes the last activity date" do
+ expect(helper.project_list_cache_key(project)).to include(project.last_activity_date)
+ end
+
it "includes the controller name" do
expect(helper.controller).to receive(:controller_name).and_return("testcontroller")
@@ -276,7 +280,11 @@ describe ProjectsHelper do
describe '#sanitizerepo_repo_path' do
let(:project) { create(:project, :repository) }
- let(:storage_path) { Gitlab.config.repositories.storages.default.legacy_disk_path }
+ let(:storage_path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.default.legacy_disk_path
+ end
+ end
before do
allow(Settings.shared).to receive(:[]).with('path').and_return('/base/repo/export/path')
diff --git a/spec/helpers/storage_helper_spec.rb b/spec/helpers/storage_helper_spec.rb
index 4627a1e1872..c580b78c908 100644
--- a/spec/helpers/storage_helper_spec.rb
+++ b/spec/helpers/storage_helper_spec.rb
@@ -1,21 +1,25 @@
-require 'spec_helper'
+require "spec_helper"
describe StorageHelper do
- describe '#storage_counter' do
- it 'formats bytes to one decimal place' do
- expect(helper.storage_counter(1.23.megabytes)).to eq '1.2 MB'
+ describe "#storage_counter" do
+ it "formats bytes to one decimal place" do
+ expect(helper.storage_counter(1.23.megabytes)).to eq("1.2 MB")
end
- it 'does not add decimals for sizes < 1 MB' do
- expect(helper.storage_counter(23.5.kilobytes)).to eq '24 KB'
+ it "does not add decimals for sizes < 1 MB" do
+ expect(helper.storage_counter(23.5.kilobytes)).to eq("24 KB")
end
- it 'does not add decimals for zeroes' do
- expect(helper.storage_counter(2.megabytes)).to eq '2 MB'
+ it "does not add decimals for zeroes" do
+ expect(helper.storage_counter(2.megabytes)).to eq("2 MB")
end
- it 'uses commas as thousands separator' do
- expect(helper.storage_counter(100_000_000_000_000_000)).to eq '90,949.5 TB'
+ it "uses commas as thousands separator" do
+ if Gitlab.rails5?
+ expect(helper.storage_counter(100_000_000_000_000_000_000_000)).to eq("86,736.2 EB")
+ else
+ expect(helper.storage_counter(100_000_000_000_000_000)).to eq("90,949.5 TB")
+ end
end
end
end
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 9b4db774b63..ad263791cd4 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -5,10 +5,10 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import '~/boards/models/assignee';
import eventHub from '~/boards/eventhub';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import '~/boards/models/list';
import '~/boards/stores/boards_store';
import boardCard from '~/boards/components/board_card.vue';
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 46fa10e1789..3f5ed4f3d07 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -7,9 +7,9 @@ import axios from '~/lib/utils/axios_utils';
import Cookies from 'js-cookie';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/models/assignee';
import '~/boards/services/board_service';
import '~/boards/stores/boards_store';
import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data';
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index abeef272c68..05acf903933 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -5,9 +5,9 @@
import Vue from 'vue';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/models/assignee';
import '~/boards/stores/boards_store';
import '~/boards/components/issue_card_inner';
import { listObj } from './mock_data';
diff --git a/spec/javascripts/boards/issue_spec.js b/spec/javascripts/boards/issue_spec.js
index d90f9a41231..db68096e3bd 100644
--- a/spec/javascripts/boards/issue_spec.js
+++ b/spec/javascripts/boards/issue_spec.js
@@ -3,9 +3,9 @@
import Vue from 'vue';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/models/assignee';
import '~/boards/services/board_service';
import '~/boards/stores/boards_store';
import { mockBoardService } from './mock_data';
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index d5d1139de15..ac8bbb8f2a8 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -6,9 +6,9 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import _ from 'underscore';
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/models/assignee';
import '~/boards/services/board_service';
import '~/boards/stores/boards_store';
import { listObj, listObjDuplicate, boardsMockInterceptor, mockBoardService } from './mock_data';
diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js
index 797693a21aa..a234c81fadf 100644
--- a/spec/javascripts/boards/modal_store_spec.js
+++ b/spec/javascripts/boards/modal_store_spec.js
@@ -1,9 +1,9 @@
/* global ListIssue */
import '~/vue_shared/models/label';
+import '~/vue_shared/models/assignee';
import '~/boards/models/issue';
import '~/boards/models/list';
-import '~/boards/models/assignee';
import Store from '~/boards/stores/modal_store';
describe('Modal store', () => {
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index 6854b016852..9e43552f740 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -110,7 +110,7 @@ describe('Clusters Store', () => {
expect(
store.state.applications.jupyter.hostname,
- ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.xip.io`);
+ ).toEqual(`jupyter.${store.state.applications.ingress.externalIp}.nip.io`);
});
});
});
diff --git a/spec/javascripts/create_merge_request_dropdown_spec.js b/spec/javascripts/create_merge_request_dropdown_spec.js
new file mode 100644
index 00000000000..b229765a8c5
--- /dev/null
+++ b/spec/javascripts/create_merge_request_dropdown_spec.js
@@ -0,0 +1,67 @@
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
+import CreateMergeRequestDropdown from '~/create_merge_request_dropdown';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('CreateMergeRequestDropdown', () => {
+ let axiosMock;
+ let dropdown;
+
+ beforeEach(() => {
+ axiosMock = new MockAdapter(axios);
+
+ setFixtures(`
+ <div id="dummy-wrapper-element">
+ <div class="available"></div>
+ <div class="unavailable">
+ <div class="fa"></div>
+ <div class="text"></div>
+ </div>
+ <div class="js-ref"></div>
+ <div class="js-create-merge-request"></div>
+ <div class="js-create-target"></div>
+ <div class="js-dropdown-toggle"></div>
+ </div>
+ `);
+
+ const dummyElement = document.getElementById('dummy-wrapper-element');
+ dropdown = new CreateMergeRequestDropdown(dummyElement);
+ dropdown.refsPath = `${TEST_HOST}/dummy/refs?search=`;
+ });
+
+ afterEach(() => {
+ axiosMock.restore();
+ });
+
+ describe('getRef', () => {
+ it('escapes branch names correctly', done => {
+ const endpoint = `${dropdown.refsPath}contains%23hash`;
+ spyOn(axios, 'get').and.callThrough();
+ axiosMock.onGet(endpoint).replyOnce({});
+
+ dropdown
+ .getRef('contains#hash')
+ .then(() => {
+ expect(axios.get).toHaveBeenCalledWith(endpoint);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('updateCreatePaths', () => {
+ it('escapes branch names correctly', () => {
+ dropdown.createBranchPath = `${TEST_HOST}/branches?branch_name=some-branch&issue=42`;
+ dropdown.createMrPath = `${TEST_HOST}/create_merge_request?branch_name=some-branch&ref=master`;
+
+ dropdown.updateCreatePaths('branch', 'contains#hash');
+
+ expect(dropdown.createBranchPath).toBe(
+ `${TEST_HOST}/branches?branch_name=contains%23hash&issue=42`,
+ );
+ expect(dropdown.createMrPath).toBe(
+ `${TEST_HOST}/create_merge_request?branch_name=contains%23hash&ref=master`,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/datetime_utility_spec.js b/spec/javascripts/datetime_utility_spec.js
index a8d09202154..e224ed46d18 100644
--- a/spec/javascripts/datetime_utility_spec.js
+++ b/spec/javascripts/datetime_utility_spec.js
@@ -149,23 +149,22 @@ describe('getSundays', () => {
});
});
-describe('getTimeframeWindow', () => {
- it('returns array of dates representing a timeframe based on provided length and date', () => {
- const date = new Date(2018, 0, 1);
+describe('getTimeframeWindowFrom', () => {
+ it('returns array of date objects upto provided length start with provided startDate', () => {
+ const startDate = new Date(2018, 0, 1);
const mockTimeframe = [
- new Date(2017, 9, 1),
- new Date(2017, 10, 1),
- new Date(2017, 11, 1),
new Date(2018, 0, 1),
new Date(2018, 1, 1),
- new Date(2018, 2, 31),
+ new Date(2018, 2, 1),
+ new Date(2018, 3, 1),
+ new Date(2018, 4, 31),
];
- const timeframe = datetimeUtility.getTimeframeWindow(6, date);
-
- expect(timeframe.length).toBe(6);
+ const timeframe = datetimeUtility.getTimeframeWindowFrom(startDate, 5);
+ expect(timeframe.length).toBe(5);
timeframe.forEach((timeframeItem, index) => {
- expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBeTruthy();
- expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBeTruthy();
+ console.log(timeframeItem);
+ expect(timeframeItem.getFullYear() === mockTimeframe[index].getFullYear()).toBe(true);
+ expect(timeframeItem.getMonth() === mockTimeframe[index].getMonth()).toBe(true);
expect(timeframeItem.getDate() === mockTimeframe[index].getDate()).toBeTruthy();
});
});
diff --git a/spec/javascripts/fixtures/images/green_box.png b/spec/javascripts/fixtures/images/green_box.png
new file mode 100644
index 00000000000..cd1ff9f9ade
--- /dev/null
+++ b/spec/javascripts/fixtures/images/green_box.png
Binary files differ
diff --git a/spec/javascripts/fixtures/images/red_box.png b/spec/javascripts/fixtures/images/red_box.png
new file mode 100644
index 00000000000..73b2927da0f
--- /dev/null
+++ b/spec/javascripts/fixtures/images/red_box.png
Binary files differ
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
index cc7e0a3f26d..8f7cf24c22f 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_item_spec.js
@@ -19,6 +19,7 @@ describe('Multi-file editor commit sidebar list item', () => {
vm = createComponentWithStore(Component, store, {
file: f,
actionComponent: 'stage-button',
+ activeFileKey: `staged-${f.key}`,
}).$mount();
});
@@ -89,4 +90,20 @@ describe('Multi-file editor commit sidebar list item', () => {
});
});
});
+
+ describe('is active', () => {
+ it('does not add active class when dont keys match', () => {
+ expect(vm.$el.classList).not.toContain('is-active');
+ });
+
+ it('adds active class when keys match', done => {
+ vm.keyPrefix = 'staged';
+
+ vm.$nextTick(() => {
+ expect(vm.$el.classList).toContain('is-active');
+
+ done();
+ });
+ });
+ });
});
diff --git a/spec/javascripts/ide/components/commit_sidebar/list_spec.js b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
index 54625ef90f8..6fb52378386 100644
--- a/spec/javascripts/ide/components/commit_sidebar/list_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/list_spec.js
@@ -17,6 +17,8 @@ describe('Multi-file editor commit sidebar list', () => {
action: 'stageAllChanges',
actionBtnText: 'stage all',
itemActionComponent: 'stage-button',
+ activeFileKey: 'staged-testing',
+ keyPrefix: 'staged',
});
vm.$store.state.rightPanelCollapsed = false;
diff --git a/spec/javascripts/ide/components/jobs/detail_spec.js b/spec/javascripts/ide/components/jobs/detail_spec.js
index 641ba06f653..8f8d4b9709e 100644
--- a/spec/javascripts/ide/components/jobs/detail_spec.js
+++ b/spec/javascripts/ide/components/jobs/detail_spec.js
@@ -62,6 +62,11 @@ describe('IDE jobs detail view', () => {
expect(vm.$el.querySelector('.build-loader-animation').style.display).toBe('');
});
+ it('hides output when loading', () => {
+ expect(vm.$el.querySelector('.bash')).not.toBe(null);
+ expect(vm.$el.querySelector('.bash').style.display).toBe('none');
+ });
+
it('hide loading icon when isLoading is false', done => {
vm.$store.state.pipelines.detailJob.isLoading = false;
diff --git a/spec/javascripts/ide/components/pipelines/list_spec.js b/spec/javascripts/ide/components/pipelines/list_spec.js
index 2bb5aa08c3b..68487733cb9 100644
--- a/spec/javascripts/ide/components/pipelines/list_spec.js
+++ b/spec/javascripts/ide/components/pipelines/list_spec.js
@@ -45,12 +45,15 @@ describe('IDE pipelines list', () => {
setTimeout(done);
});
- afterEach(() => {
- vm.$store.dispatch('pipelines/stopPipelinePolling');
- vm.$store.dispatch('pipelines/clearEtagPoll');
-
+ afterEach(done => {
vm.$destroy();
mock.restore();
+
+ vm.$store
+ .dispatch('pipelines/stopPipelinePolling')
+ .then(() => vm.$store.dispatch('pipelines/clearEtagPoll'))
+ .then(done)
+ .catch(done.fail);
});
it('renders pipeline data', () => {
diff --git a/spec/javascripts/ide/components/repo_commit_section_spec.js b/spec/javascripts/ide/components/repo_commit_section_spec.js
index 5e3e00a180b..531bcd6e540 100644
--- a/spec/javascripts/ide/components/repo_commit_section_spec.js
+++ b/spec/javascripts/ide/components/repo_commit_section_spec.js
@@ -56,7 +56,7 @@ describe('RepoCommitSection', () => {
vm.$store.state.entries[f.path] = f;
});
- return vm.$mount();
+ return vm;
}
beforeEach(done => {
@@ -64,6 +64,10 @@ describe('RepoCommitSection', () => {
vm = createComponent();
+ spyOn(vm, 'openPendingTab').and.callThrough();
+
+ vm.$mount();
+
spyOn(service, 'getTreeData').and.returnValue(
Promise.resolve({
headers: {
@@ -98,6 +102,7 @@ describe('RepoCommitSection', () => {
store.state.noChangesStateSvgPath = 'nochangessvg';
store.state.committedStateSvgPath = 'svg';
+ vm.$destroy();
vm = createComponentWithStore(Component, store).$mount();
expect(vm.$el.querySelector('.js-empty-state').textContent.trim()).toContain('No changes');
@@ -176,5 +181,12 @@ describe('RepoCommitSection', () => {
expect(store.state.openFiles.length).toBe(1);
expect(store.state.openFiles[0].pending).toBe(true);
});
+
+ it('calls openPendingTab', () => {
+ expect(vm.openPendingTab).toHaveBeenCalledWith({
+ file: vm.lastOpenedFile,
+ keyPrefix: 'unstaged',
+ });
+ });
});
});
diff --git a/spec/javascripts/ide/components/repo_editor_spec.js b/spec/javascripts/ide/components/repo_editor_spec.js
index d3f80e6f9c0..d318521d0a0 100644
--- a/spec/javascripts/ide/components/repo_editor_spec.js
+++ b/spec/javascripts/ide/components/repo_editor_spec.js
@@ -3,7 +3,6 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
import repoEditor from '~/ide/components/repo_editor.vue';
-import monacoLoader from '~/ide/monaco_loader';
import Editor from '~/ide/lib/editor';
import { activityBarViews } from '~/ide/constants';
import { createComponentWithStore } from '../../helpers/vue_mount_component_helper';
@@ -25,13 +24,10 @@ describe('RepoEditor', () => {
f.tempFile = true;
vm.$store.state.openFiles.push(f);
Vue.set(vm.$store.state.entries, f.path, f);
- vm.monaco = true;
vm.$mount();
- monacoLoader(['vs/editor/editor.main'], () => {
- setTimeout(done, 0);
- });
+ Vue.nextTick(() => setTimeout(done));
});
afterEach(() => {
diff --git a/spec/javascripts/ide/lib/common/model_manager_spec.js b/spec/javascripts/ide/lib/common/model_manager_spec.js
index c00d590c580..38ffa317e8e 100644
--- a/spec/javascripts/ide/lib/common/model_manager_spec.js
+++ b/spec/javascripts/ide/lib/common/model_manager_spec.js
@@ -1,18 +1,12 @@
-/* global monaco */
import eventHub from '~/ide/eventhub';
-import monacoLoader from '~/ide/monaco_loader';
import ModelManager from '~/ide/lib/common/model_manager';
import { file } from '../../helpers';
describe('Multi-file editor library model manager', () => {
let instance;
- beforeEach(done => {
- monacoLoader(['vs/editor/editor.main'], () => {
- instance = new ModelManager(monaco);
-
- done();
- });
+ beforeEach(() => {
+ instance = new ModelManager();
});
afterEach(() => {
diff --git a/spec/javascripts/ide/lib/common/model_spec.js b/spec/javascripts/ide/lib/common/model_spec.js
index c278bf92b08..f096e06f43c 100644
--- a/spec/javascripts/ide/lib/common/model_spec.js
+++ b/spec/javascripts/ide/lib/common/model_spec.js
@@ -1,23 +1,17 @@
-/* global monaco */
import eventHub from '~/ide/eventhub';
-import monacoLoader from '~/ide/monaco_loader';
import Model from '~/ide/lib/common/model';
import { file } from '../../helpers';
describe('Multi-file editor library model', () => {
let model;
- beforeEach(done => {
+ beforeEach(() => {
spyOn(eventHub, '$on').and.callThrough();
- monacoLoader(['vs/editor/editor.main'], () => {
- const f = file('path');
- f.mrChange = { diff: 'ABC' };
- f.baseRaw = 'test';
- model = new Model(monaco, f);
-
- done();
- });
+ const f = file('path');
+ f.mrChange = { diff: 'ABC' };
+ f.baseRaw = 'test';
+ model = new Model(f);
});
afterEach(() => {
@@ -38,7 +32,7 @@ describe('Multi-file editor library model', () => {
const f = file('path');
model.dispose();
- model = new Model(monaco, f, {
+ model = new Model(f, {
...f,
content: '123 testing',
});
diff --git a/spec/javascripts/ide/lib/decorations/controller_spec.js b/spec/javascripts/ide/lib/decorations/controller_spec.js
index e1c4ca570b6..a112361e0d1 100644
--- a/spec/javascripts/ide/lib/decorations/controller_spec.js
+++ b/spec/javascripts/ide/lib/decorations/controller_spec.js
@@ -1,6 +1,4 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import editor from '~/ide/lib/editor';
+import Editor from '~/ide/lib/editor';
import DecorationsController from '~/ide/lib/decorations/controller';
import Model from '~/ide/lib/common/model';
import { file } from '../../helpers';
@@ -10,16 +8,12 @@ describe('Multi-file editor library decorations controller', () => {
let controller;
let model;
- beforeEach(done => {
- monacoLoader(['vs/editor/editor.main'], () => {
- editorInstance = editor.create(monaco);
- editorInstance.createInstance(document.createElement('div'));
+ beforeEach(() => {
+ editorInstance = Editor.create();
+ editorInstance.createInstance(document.createElement('div'));
- controller = new DecorationsController(editorInstance);
- model = new Model(monaco, file('path'));
-
- done();
- });
+ controller = new DecorationsController(editorInstance);
+ model = new Model(file('path'));
});
afterEach(() => {
diff --git a/spec/javascripts/ide/lib/diff/controller_spec.js b/spec/javascripts/ide/lib/diff/controller_spec.js
index fd8ab3b4f1d..96abd1dcd9e 100644
--- a/spec/javascripts/ide/lib/diff/controller_spec.js
+++ b/spec/javascripts/ide/lib/diff/controller_spec.js
@@ -1,6 +1,5 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import editor from '~/ide/lib/editor';
+import { Range } from 'monaco-editor';
+import Editor from '~/ide/lib/editor';
import ModelManager from '~/ide/lib/common/model_manager';
import DecorationsController from '~/ide/lib/decorations/controller';
import DirtyDiffController, { getDiffChangeType, getDecorator } from '~/ide/lib/diff/controller';
@@ -14,20 +13,16 @@ describe('Multi-file editor library dirty diff controller', () => {
let decorationsController;
let model;
- beforeEach(done => {
- monacoLoader(['vs/editor/editor.main'], () => {
- editorInstance = editor.create(monaco);
- editorInstance.createInstance(document.createElement('div'));
+ beforeEach(() => {
+ editorInstance = Editor.create();
+ editorInstance.createInstance(document.createElement('div'));
- modelManager = new ModelManager(monaco);
- decorationsController = new DecorationsController(editorInstance);
+ modelManager = new ModelManager();
+ decorationsController = new DecorationsController(editorInstance);
- model = modelManager.addModel(file('path'));
+ model = modelManager.addModel(file('path'));
- controller = new DirtyDiffController(modelManager, decorationsController);
-
- done();
- });
+ controller = new DirtyDiffController(modelManager, decorationsController);
});
afterEach(() => {
@@ -170,7 +165,7 @@ describe('Multi-file editor library dirty diff controller', () => {
[],
[
{
- range: new monaco.Range(1, 1, 1, 1),
+ range: new Range(1, 1, 1, 1),
options: {
isWholeLine: true,
linesDecorationsClassName: 'dirty-diff dirty-diff-modified',
diff --git a/spec/javascripts/ide/lib/editor_spec.js b/spec/javascripts/ide/lib/editor_spec.js
index b88a12264ca..c1932284d53 100644
--- a/spec/javascripts/ide/lib/editor_spec.js
+++ b/spec/javascripts/ide/lib/editor_spec.js
@@ -1,6 +1,5 @@
-/* global monaco */
-import monacoLoader from '~/ide/monaco_loader';
-import editor from '~/ide/lib/editor';
+import { editor as monacoEditor } from 'monaco-editor';
+import Editor from '~/ide/lib/editor';
import { file } from '../helpers';
describe('Multi-file editor library', () => {
@@ -8,18 +7,14 @@ describe('Multi-file editor library', () => {
let el;
let holder;
- beforeEach(done => {
+ beforeEach(() => {
el = document.createElement('div');
holder = document.createElement('div');
el.appendChild(holder);
document.body.appendChild(el);
- monacoLoader(['vs/editor/editor.main'], () => {
- instance = editor.create(monaco);
-
- done();
- });
+ instance = Editor.create();
});
afterEach(() => {
@@ -29,20 +24,20 @@ describe('Multi-file editor library', () => {
});
it('creates instance of editor', () => {
- expect(editor.editorInstance).not.toBeNull();
+ expect(Editor.editorInstance).not.toBeNull();
});
it('creates instance returns cached instance', () => {
- expect(editor.create(monaco)).toEqual(instance);
+ expect(Editor.create()).toEqual(instance);
});
describe('createInstance', () => {
it('creates editor instance', () => {
- spyOn(instance.monaco.editor, 'create').and.callThrough();
+ spyOn(monacoEditor, 'create').and.callThrough();
instance.createInstance(holder);
- expect(instance.monaco.editor.create).toHaveBeenCalled();
+ expect(monacoEditor.create).toHaveBeenCalled();
});
it('creates dirty diff controller', () => {
@@ -60,11 +55,11 @@ describe('Multi-file editor library', () => {
describe('createDiffInstance', () => {
it('creates editor instance', () => {
- spyOn(instance.monaco.editor, 'createDiffEditor').and.callThrough();
+ spyOn(monacoEditor, 'createDiffEditor').and.callThrough();
instance.createDiffInstance(holder);
- expect(instance.monaco.editor.createDiffEditor).toHaveBeenCalledWith(holder, {
+ expect(monacoEditor.createDiffEditor).toHaveBeenCalledWith(holder, {
model: null,
contextmenu: true,
minimap: {
diff --git a/spec/javascripts/ide/monaco_loader_spec.js b/spec/javascripts/ide/monaco_loader_spec.js
deleted file mode 100644
index 7ab315aa8c8..00000000000
--- a/spec/javascripts/ide/monaco_loader_spec.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import monacoContext from 'monaco-editor/dev/vs/loader';
-import monacoLoader from '~/ide/monaco_loader';
-
-describe('MonacoLoader', () => {
- it('calls require.config and exports require', () => {
- expect(monacoContext.require.getConfig()).toEqual(
- jasmine.objectContaining({
- paths: {
- vs: `${__webpack_public_path__}monaco-editor/vs`, // eslint-disable-line camelcase
- },
- }),
- );
- expect(monacoLoader).toBe(monacoContext.require);
- });
-});
diff --git a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
index d178a44b76a..03ec08d05c3 100644
--- a/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/merge_requests/actions_spec.js
@@ -199,28 +199,39 @@ describe('IDE merge requests actions', () => {
});
it('commits reset mutations and actions', done => {
- testAction(
- openMergeRequest,
- { projectPath: 'gitlab-org/gitlab-ce', id: '1' },
- mockedState,
- [
- { type: 'CLEAR_PROJECTS' },
- { type: 'SET_CURRENT_MERGE_REQUEST', payload: '1' },
- { type: 'RESET_OPEN_FILES' },
- ],
- [
- { type: 'pipelines/stopPipelinePolling' },
- { type: 'pipelines/clearEtagPoll' },
- { type: 'pipelines/resetLatestPipeline' },
- { type: 'setCurrentBranchId', payload: '' },
- ],
- done,
- );
+ const commit = jasmine.createSpy();
+ const dispatch = jasmine.createSpy().and.returnValue(Promise.resolve());
+ openMergeRequest({ commit, dispatch }, { projectPath: 'gitlab-org/gitlab-ce', id: '1' });
+
+ setTimeout(() => {
+ expect(commit.calls.argsFor(0)).toEqual(['CLEAR_PROJECTS', null, { root: true }]);
+ expect(commit.calls.argsFor(1)).toEqual(['SET_CURRENT_MERGE_REQUEST', '1', { root: true }]);
+ expect(commit.calls.argsFor(2)).toEqual(['RESET_OPEN_FILES', null, { root: true }]);
+
+ expect(dispatch.calls.argsFor(0)).toEqual([
+ 'pipelines/resetLatestPipeline',
+ null,
+ { root: true },
+ ]);
+ expect(dispatch.calls.argsFor(1)).toEqual(['setCurrentBranchId', '', { root: true }]);
+ expect(dispatch.calls.argsFor(2)).toEqual([
+ 'pipelines/stopPipelinePolling',
+ null,
+ { root: true },
+ ]);
+ expect(dispatch.calls.argsFor(3)).toEqual([
+ 'pipelines/clearEtagPoll',
+ null,
+ { root: true },
+ ]);
+
+ done();
+ });
});
it('pushes new route', () => {
openMergeRequest(
- { commit() {}, dispatch() {} },
+ { commit() {}, dispatch: () => Promise.resolve() },
{ projectPath: 'gitlab-org/gitlab-ce', id: '1' },
);
diff --git a/spec/javascripts/ide/stores/mutations/file_spec.js b/spec/javascripts/ide/stores/mutations/file_spec.js
index e83961fcedc..52f83be8e8c 100644
--- a/spec/javascripts/ide/stores/mutations/file_spec.js
+++ b/spec/javascripts/ide/stores/mutations/file_spec.js
@@ -152,6 +152,53 @@ describe('IDE store file mutations', () => {
expect(localFile.mrChange.diff).toBe('ABC');
});
+
+ it('has diffMode replaced by default', () => {
+ mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
+ file: localFile,
+ mrChange: {
+ diff: 'ABC',
+ },
+ });
+
+ expect(localFile.mrChange.diffMode).toBe('replaced');
+ });
+
+ it('has diffMode new', () => {
+ mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
+ file: localFile,
+ mrChange: {
+ diff: 'ABC',
+ new_file: true,
+ },
+ });
+
+ expect(localFile.mrChange.diffMode).toBe('new');
+ });
+
+ it('has diffMode deleted', () => {
+ mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
+ file: localFile,
+ mrChange: {
+ diff: 'ABC',
+ deleted_file: true,
+ },
+ });
+
+ expect(localFile.mrChange.diffMode).toBe('deleted');
+ });
+
+ it('has diffMode renamed', () => {
+ mutations.SET_FILE_MERGE_REQUEST_CHANGE(localState, {
+ file: localFile,
+ mrChange: {
+ diff: 'ABC',
+ renamed_file: true,
+ },
+ });
+
+ expect(localFile.mrChange.diffMode).toBe('renamed');
+ });
});
describe('DISCARD_FILE_CHANGES', () => {
diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js
index 25ca8eb6c0b..dd025255bd1 100644
--- a/spec/javascripts/jobs/mock_data.js
+++ b/spec/javascripts/jobs/mock_data.js
@@ -20,7 +20,7 @@ export default {
group: 'success',
has_details: true,
details_path: '/root/ci-mock/-/jobs/4757',
- favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -78,7 +78,7 @@ export default {
group: 'success',
has_details: true,
details_path: '/root/ci-mock/pipelines/140',
- favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ favicon: '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
duration: 6,
finished_at: '2017-06-01T17:32:00.042Z',
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index 27f06573432..2d7cc3443cf 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -2,6 +2,7 @@
import axios from '~/lib/utils/axios_utils';
import * as commonUtils from '~/lib/utils/common_utils';
import MockAdapter from 'axios-mock-adapter';
+import { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from './mock_data';
describe('common_utils', () => {
describe('parseUrl', () => {
@@ -395,6 +396,7 @@ describe('common_utils', () => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
favicon.setAttribute('href', 'default/favicon');
+ favicon.setAttribute('data-default-href', 'default/favicon');
document.body.appendChild(favicon);
});
@@ -413,7 +415,7 @@ describe('common_utils', () => {
beforeEach(() => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
- favicon.setAttribute('href', 'default/favicon');
+ favicon.setAttribute('data-original-href', 'default/favicon');
document.body.appendChild(favicon);
});
@@ -421,12 +423,43 @@ describe('common_utils', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should reset page favicon to tanuki', () => {
+ it('should reset page favicon to the default icon', () => {
+ const favicon = document.getElementById('favicon');
+ favicon.setAttribute('href', 'new/favicon');
commonUtils.resetFavicon();
expect(document.getElementById('favicon').getAttribute('href')).toEqual('default/favicon');
});
});
+ describe('createOverlayIcon', () => {
+ it('should return the favicon with the overlay', (done) => {
+ commonUtils.createOverlayIcon(faviconDataUrl, overlayDataUrl).then((url) => {
+ expect(url).toEqual(faviconWithOverlayDataUrl);
+ done();
+ });
+ });
+ });
+
+ describe('setFaviconOverlay', () => {
+ beforeEach(() => {
+ const favicon = document.createElement('link');
+ favicon.setAttribute('id', 'favicon');
+ favicon.setAttribute('data-original-href', faviconDataUrl);
+ document.body.appendChild(favicon);
+ });
+
+ afterEach(() => {
+ document.body.removeChild(document.getElementById('favicon'));
+ });
+
+ it('should set page favicon to provided favicon overlay', (done) => {
+ commonUtils.setFaviconOverlay(overlayDataUrl).then(() => {
+ expect(document.getElementById('favicon').getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
+ done();
+ });
+ });
+ });
+
describe('setCiStatusFavicon', () => {
const BUILD_URL = `${gl.TEST_HOST}/frontend-fixtures/builds-project/-/jobs/1/status.json`;
let mock;
@@ -434,6 +467,8 @@ describe('common_utils', () => {
beforeEach(() => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
+ favicon.setAttribute('href', 'null');
+ favicon.setAttribute('data-original-href', faviconDataUrl);
document.body.appendChild(favicon);
mock = new MockAdapter(axios);
});
@@ -449,7 +484,7 @@ describe('common_utils', () => {
commonUtils.setCiStatusFavicon(BUILD_URL)
.then(() => {
const favicon = document.getElementById('favicon');
- expect(favicon.getAttribute('href')).toEqual('null');
+ expect(favicon.getAttribute('href')).toEqual(faviconDataUrl);
done();
})
// Error is already caught in catch() block of setCiStatusFavicon,
@@ -458,16 +493,14 @@ describe('common_utils', () => {
});
it('should set page favicon to CI status favicon based on provided status', (done) => {
- const FAVICON_PATH = '//icon_status_success';
-
mock.onGet(BUILD_URL).reply(200, {
- favicon: FAVICON_PATH,
+ favicon: overlayDataUrl,
});
commonUtils.setCiStatusFavicon(BUILD_URL)
.then(() => {
const favicon = document.getElementById('favicon');
- expect(favicon.getAttribute('href')).toEqual(FAVICON_PATH);
+ expect(favicon.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
done();
})
.catch(done.fail);
diff --git a/spec/javascripts/lib/utils/mock_data.js b/spec/javascripts/lib/utils/mock_data.js
new file mode 100644
index 00000000000..fd0d62b751f
--- /dev/null
+++ b/spec/javascripts/lib/utils/mock_data.js
@@ -0,0 +1,5 @@
+export const faviconDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAACcFBMVEX////iQyniQyniQyniQyniQyniQyniQyniQynhRiriQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniRCniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQyniQynhQiniQiniQiniQinhQinpUSjqUSjqTyjqTyjqTyjlSCniRCniQynjRCjqTyjsZSjrWyj8oib9kSb8pyb9pib8oyb8fyb3ZSb4Zib8fCb8oyb8oyb8oyb8pCb8cSbiQyn7bCb8cib8oyb8oSb8bSbtVSjpTij8nyb8oyb8oyb8lCb2Yyf3ZCf8mCb8oyb8oyb8oyb8iib8bSbiRCn8gyb8oyb8eCbpTinrUSj8oyb8oyb8oyb8pSb8bib4Zif0YCf8byb8oyb8oyb8oyb7oib8oyb8nCbjRSn9bib8ayb8nib8oyb8oyb8oyb8kSbpTyjpTyj8jib8oyb8oyb8oyb8fib0Xyf2ZSb8gCb8oyb6pSb8oyb8dib+cCbgQCnjRSn8cCb8oib8oyb8oyb8oybqUCjnSyn8bCb8oyb8oyb8oyb8myb2YyfyXyf8oyb8oyb8hibhQSn+bib8iSb8oyb8qCb+fSbmSSnqTyj8oib9pCb1YifxXyf7pSb8oCb8pCb+mCb0fCf8pSb7hSXvcSjiQyniQinqTyj9kCb9bib9byb+cCbqUSjiRCnsVCj+cSb8pib8bCb8bSbgQCn7bCb8bibjRSn8oyb8ayb8oib8aib8pCbjRCn8pybhQinhQSn8pSb7ayb7aSb6aib8eib///8IbM+7AAAAr3RSTlMBA3NtX2vT698HGQcRLwWLiXnv++3V+eEd/R8HE2V/Y5HjyefdFw99YWfJ+/3nwQP78/HvX1VTQ/kdA2HzbQXj9fX79/3DGf379/33T/v99/f7ba33+/f1+9/18/v59V339flzF/H9+fX3/fMhBwOh9/v5/fmvBV/z+fP3Awnp9/f38+UFgff7+/37+4c77/f7/flFz/f59dFr7/v98Wnr+/f3I5/197EDBU1ZAwUD8/kLUwAAAAFiS0dEAIgFHUgAAAAHdElNRQfhBQoLHiBV6/1lAAACHUlEQVQ4y41TZXsTQRCe4FAIUigN7m7FXY+iLRQKBG2x4g7BjhZ3Le7uMoEkFJprwyQk0CC/iZnNhUZaHt4vt6/szO7cHcD/wFKjZrJWq3YMq1M3eVc9rFzXR2yQkuA3RGxkjZLGiEk9miA2tURJs1RsnhhokYYtzaU13WZDbBVnW1sjo43J2vI6tZ0lLtFeAh1M0lECneI7dGYtrUtk3RUVIKaEJR25qw27yT0s3W0qEHuPlB4RradivXo7GX36xnbo51SQ+fWHARmCgYMGDxkaxbD3SssYPmIkwKgPLrfA87EETTg/fVaSa/SYsQDjSsd7DcGEsr+BieVKmaRNBsjUtClTfUI900y/5Mt05c8oJQKYSURZ2UqYFa0w283M588JEM2BuRwI5EqT8nmmXzZf4l8XsGNfCIv4QcHFklhiBpaqAsuC4tghj+ySyOdjeJYrP7RCCuR/E5tWAqxaLcmCNSyujdxjHZdbn8UHoA0bN/GoNm8hjQJb/ZzYpo6w3TB27JRduxxqrA7YzbWCezixN8RD2Oc2/Ptlfx7o5uT1A4XMiwzj4HfEikNe7+Ew0ZGjeuW70eEYaeHjxomTiKd++E4XnKGz8d+HDufOB3Ky3RcwdNF1qZiKLyf/B44r2tWf15wV143cwI2qfi8dbtKtX6Hbd+6G74EDqkTm/QcPH/0ufFyNLXjy9NnzF9Xb8BJevYY38C+8fZcg/AF3QTYemVkCwwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0xMFQxMTozMDozMiswMjowMMzup8UAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMTBUMTE6MzA6MzIrMDI6MDC9sx95AAAAAElFTkSuQmCC';
+
+export const overlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAA85JREFUWAntVllIVGEUPv/9b46O41KplYN7PeRkti8TjQlhCUGh3MmeQugpIsGKAi2soIcIooiohxYKK2daqDAlIpIiWwxtQaJcaHE0d5tMrbn37z9XRqfR0TvVW56Hudf//uec72zfEWBCJjIwkYGJDPzvGSD/KgExN3Oi2Q+2DJgSDYQEMwItVGH1iZGmJw/Si1y+/PwVAMYYib22MYc/8hVQFgKDEfYoId0KYzagAQebsos/ewMZoeB9wdffcTYpQSaCTWHKoqSQaDk7zkIt0+aCUR8BelEHrf3dUNv9AcqbnsHtT5UKB/hTASh0SLYjnjb/CIDRJi0XiFAaJOpCD8zLpdb4NB66b1OfelthX815dtdRRfiti2aAXLvVLiMQ6olGyztGDkSo4JGGXk8/QFdGpYzpHG2GBQTDhtgVhPEaVbbVpvI6GJz22rv4TcAfrYI1x7Rj5MWWAppomKFVVb2302SFzUkZHAbkG+0b1+Gh77yNYjrmqnWTrLBLRxdvBWv8qlFujH/kYjJYyvLkj71t78zAUvzMAMnHhpN4zf9UREJhd8omyssxu1IgazQDwDnHUcNuH6vhPIE1fmuBzHt74Hn7W89jWGtcAjoaIDOFrdcMYJBkgOCoaRF0Lj0oglddDbCj6tRvKjphEpgjkzEQs2YAKsNxMzjn3nKurhzK+Ly7xe28ua8TwgMMcHJZnvvT0BPtEEKM4tDJ+C8GvIIk4ylINIXVZ0EUKJxYuh3mhCeokbudl6TtVc88dfBdLwbyaWB6zQCYQJpBYSrDGQxBQ/ZWRM2B+VNmQnVnHWx7elyNuL2/R336co7KyJR8CL9oLgEuFlREevWUkEl6uGwpVEG4FBm0OEf9N10NMgPlvWYAuNVwsWDKvcUNYsHUWTCZ13ysyFEXe6TO6aC8CUr9IiK+A05TQrc8yjwmxARHeeMAPlfQJw+AQRwu0YhL/GDXi9NwufG+S8dYkuYMqIb4SsWthotlNMOUCOM6r+G9cqXxPmd1dqrBav/o1zJy2l5/NUjJA/VORwYuFnOUaTQcPs9wMqwV++Xv8oADxKAcZ8nLPr8AoGW+xR6HSqYk3GodAz2QNj0V+Gr26dT9ASNH5239Pf0gktVNWZca8ZvfAFBprWS6hSu1pqt++Y0PD+WIwDAhIWQGtzvSHDbcodfFUFB9hg1Gjs5LXqIdFL+acFBl+FddqYwdxsWC3I70OvgfUaA65zhq2O2c8VxYcyIGFTVlXegYtvCXANCQZJMobjVcLMjtSK/IcEgyOOe8Ve5w7ryKDefp2P3+C/5ohv8HZmVLAAAAAElFTkSuQmCC';
+
+export const faviconWithOverlayDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAGt0lEQVRYR8WWf3DT5R3H35/vN0lDiCztSuiPAEnTFhSOUamIzFGokwMH55g0E845yjbP6+4qIoiHY6JjnHLeRI6h9jgQpQcD3ImH2u00eHBwjGthKLI1TSm26S8oKYVS0vT7/X52z7ckTUhqC/yx55/kks/zeb+ez6/nIWYm/B8XJQB4SGq6lL+CJA47vvRtvWs2D0nNl/Kf0qBZxx6u23arv0QAAIHivK8BynB4ffa7BgDQXJx/ngGnw+uThgTwP5ZnMocoJAxVxZDVZ+0L5n5WF75TkMafjLdJxpSg2E+gqW1X7zk3rbpaifhLiEBgTv4mEFbpBoTyu01DoDj/dQAv9rvjtdnp/k3Yx9rgAMV5QYCsAAwAgg6vL/1OTy/2BYrzzwLIBWACuNHhrXPG+otGoKaw0JA58kqGJtOFfgPS8yWT4sz88nzj7UIIfz+wd0mRdEZPLMnp2V/8R0+JrhLbBYFHJvwWzBUxYgqYNzhG+zfEhm24MIE5ectBtP0W+y0Or29FcoDifHFSRxwAcMrh9c0YrmisXaA4r0V0U8xvopgDDq9PpCQ+Ag0/zbEbNUNbMiG9fTwkDTsKHpJa2t1Zmiw1AqLg+tMZ+R6WVVtnZ2qP6Ib+FIjh05G3lsDrB4xjUIbRDeM+WZLJYZ4B1rKMzKPm/fdyzs9qg6WT225IMnPcuYjxbPZhn57qaA00zc4/QYT7b1b/wAZmDYSLjsN1WcmiM+6jXz7JTCs1aNPASBjrtrCGOXVBLK9ph72772bc0REZcsQlkEVoOxblhaFBH0Bxi6GBWFNC8gpV0XqYSe/hI85R9o1zxr/QaZbdbmuW9oRzljRrzBRkW9JhMaTgYugKzl35DlXNJ/Fp43FImoZnz7T0ln7bLihM0g85N627vkWPgLrbvYyCvAP1+rRIWETA5QsyQlcJYOCbMRasWpALtljwSsFyeJxFYsoNWqdN1y/ildM78Y/WGjxx8TL+ol3oluy8VupKe7cfoNLdCJkdqEUPOmBJ5ksJoae91mBps5lQ6pkIm20MPiz6A3KsmcNukDe/3Ye3zh3A77Q2XqcGjslLz88i/nB8pkpSoL8nAFSTBpUN4qSxS5KB5jOGUOniCebmzFQcevSN2xKP+Fp7ajt21f8TOxU/5i45JZFS6XwcTB9HxZgUnGTRNgk31x5jet+aGU7jWw+UweOcPeyTxxoqrGL25+UwdjehSvnmOVIqcz4C8y8GAABcQwjnYI5NheikhQWT+EZmDh2ev/l7cz4U2cGmYyg78TYqVH87Kbtd1wFY4hsVQAt14zu2RiDaTUZMf/BHWD35STx37wDv94k1dLeh7MRmvDZ1GR5Inxg17dX6MPnjZfh5X6tGSqXrV2B8ACIx98UNGOlV4CxCuA6zqIeq9FQ8c68bhx7ZiIK06CQdVF+Il3y1Hq03gnDfk4Uj8zbH2T51dCPOtlW39Q+iPTl2VSMfwKPiKw8aTuhgpl1Zdqxzj8PphRWwm21xZjv9VcgYkYb52dP132PFbSYr/la0DpNtrrg9a2oqsKfB2zlwG+4nSe1z7QDjaQBi2Eh6J4QRwimYt43LwOsuB2oX7YLVMCLqTAya3xx/EwZJxtYHy3WhyMkHExebXz3zAbbXfdo7AFBRaMAz1Ypa6XoaoPejKRGteZm6D3SlWVdOcOHo/Lfj2u9aXw+WHNmA00G/DiFEO0Jd+meyk0fIf/+vLfik6Xhj4qN0v7i5HCY1bBQPk+ij9GSzNbzYNdH03kMrscARfzvHQgiBocSFTVHVCrW+u+WrpK9iCIgS1rRK93oG/1GkRJVIup8KMNs1Sw/1rUtALD36ZzRca8XeJDmPtRc18vDn5SCJViYHENY3IZTK3JkE7RAYtpdkp3bAaJeOzN+CsSMTX+wqa7ih9sbVSLI2WV3znihAJYXZPThA7M6KQoM2MniyhUxTioxTpKLMadjx8Jqh5k3S//8d9GOh92XWmP/aXLKvfHgA0ZTklL0jj9m6UR6L5+9bjFWTPLcFIWbCY1+8pHb0drWybJ4aWLQrODyAWJndzoyylNyGg0hL+bV7Ll4rKIWB5CFBxMlLj21SL4W6QjDQjwOL9n4tNt0+AADPfo+UqgXPHJLSJrkso7F6ylLMy56OFMmYACIKblvtQext8Iqp0swyLYiI3zEAbs6Ml3cXv/p3Y+ryq5KcnSKb1Jmj75P7X0Rm/UV0tvO86r/WIhORwszvkmHEehH2WMo7ikDUQUWhoaIG+NNc96Os8eMEmklE2Qy2ANTO0OrA+CwFOFBfsq8pWZ7+B25aDBxvPp+QAAAAAElFTkSuQmCC';
diff --git a/spec/javascripts/lib/utils/url_utility_spec.js b/spec/javascripts/lib/utils/url_utility_spec.js
new file mode 100644
index 00000000000..c7f4092911c
--- /dev/null
+++ b/spec/javascripts/lib/utils/url_utility_spec.js
@@ -0,0 +1,29 @@
+import { webIDEUrl } from '~/lib/utils/url_utility';
+
+describe('URL utility', () => {
+ describe('webIDEUrl', () => {
+ afterEach(() => {
+ gon.relative_url_root = '';
+ });
+
+ describe('without relative_url_root', () => {
+ it('returns IDE path with route', () => {
+ expect(webIDEUrl('/gitlab-org/gitlab-ce/merge_requests/1')).toBe(
+ '/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1',
+ );
+ });
+ });
+
+ describe('with relative_url_root', () => {
+ beforeEach(() => {
+ gon.relative_url_root = '/gitlab';
+ });
+
+ it('returns IDE path with route', () => {
+ expect(webIDEUrl('/gitlab/gitlab-org/gitlab-ce/merge_requests/1')).toBe(
+ '/gitlab/-/ide/project/gitlab-org/gitlab-ce/merge_requests/1',
+ );
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/comment_form_spec.js b/spec/javascripts/notes/components/comment_form_spec.js
index 224debbeff6..a7d1e4331eb 100644
--- a/spec/javascripts/notes/components/comment_form_spec.js
+++ b/spec/javascripts/notes/components/comment_form_spec.js
@@ -84,7 +84,7 @@ describe('issue_comment_form component', () => {
it('should render textarea with placeholder', () => {
expect(
vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'),
- ).toEqual('Write a comment or drag your files here...');
+ ).toEqual('Write a comment or drag your files here…');
});
it('should make textarea disabled while requesting', (done) => {
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index 0e792eee5e9..d494c63ff11 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -106,7 +106,7 @@ describe('note_app', () => {
expect(vm.$el.querySelector('.js-main-target-form').tagName).toEqual('FORM');
expect(
vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'),
- ).toEqual('Write a comment or drag your files here...');
+ ).toEqual('Write a comment or drag your files here…');
});
it('should render form comment button as disabled', () => {
@@ -129,7 +129,7 @@ describe('note_app', () => {
expect(vm.$el.querySelector('.js-main-target-form').tagName).toEqual('FORM');
expect(
vm.$el.querySelector('.js-main-target-form textarea').getAttribute('placeholder'),
- ).toEqual('Write a comment or drag your files here...');
+ ).toEqual('Write a comment or drag your files here…');
});
});
diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js
index f841a408d09..413d4f69434 100644
--- a/spec/javascripts/notes/components/note_form_spec.js
+++ b/spec/javascripts/notes/components/note_form_spec.js
@@ -49,7 +49,7 @@ describe('issue_note_form component', () => {
it('should render text area with placeholder', () => {
expect(
vm.$el.querySelector('textarea').getAttribute('placeholder'),
- ).toEqual('Write a comment or drag your files here...');
+ ).toEqual('Write a comment or drag your files here…');
});
it('should link to markdown docs', () => {
diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js
index 70eba98e939..9e25a4b3fed 100644
--- a/spec/javascripts/pipelines/graph/mock_data.js
+++ b/spec/javascripts/pipelines/graph/mock_data.js
@@ -20,7 +20,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/pipelines/123',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
duration: 9,
finished_at: '2017-04-19T14:30:27.542Z',
@@ -40,7 +40,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/builds/4153',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -65,7 +65,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/builds/4153',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -85,7 +85,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/pipelines/123#test',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
path: '/root/ci-mock/pipelines/123#test',
dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=test',
@@ -105,7 +105,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/builds/4166',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -130,7 +130,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/builds/4166',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -152,7 +152,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/builds/4159',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -177,7 +177,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/builds/4159',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
action: {
icon: 'retry',
title: 'Retry',
@@ -197,7 +197,7 @@ export default {
has_details: true,
details_path: '/root/ci-mock/pipelines/123#deploy',
favicon:
- '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
+ '/assets/ci_favicons/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.png',
},
path: '/root/ci-mock/pipelines/123#deploy',
dropdown_path: '/root/ci-mock/pipelines/123/stage.json?stage=deploy',
diff --git a/spec/javascripts/shortcuts_issuable_spec.js b/spec/javascripts/shortcuts_issuable_spec.js
index d73608ed0ed..b10d8be6781 100644
--- a/spec/javascripts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/shortcuts_issuable_spec.js
@@ -66,7 +66,7 @@ describe('ShortcutsIssuable', function () {
});
describe('with a multi-line selection', () => {
it('quotes the selected lines as a group', () => {
- stubSelection('<p>Selected line one.</p>\n\n<p>Selected line two.</p>\n\n<p>Selected line three.</p>');
+ stubSelection('<p>Selected line one.</p>\n<p>Selected line two.</p>\n<p>Selected line three.</p>');
this.shortcut.replyWithSelectedText(true);
expect($(this.selector).val()).toBe('> Selected line one.\n>\n> Selected line two.\n>\n> Selected line three.\n\n');
});
diff --git a/spec/javascripts/test_constants.js b/spec/javascripts/test_constants.js
index df59195e9f6..a820dd2d09c 100644
--- a/spec/javascripts/test_constants.js
+++ b/spec/javascripts/test_constants.js
@@ -2,3 +2,6 @@ export const FIXTURES_PATH = '/base/spec/javascripts/fixtures';
export const TEST_HOST = 'http://test.host';
export const DUMMY_IMAGE_URL = `${FIXTURES_PATH}/one_white_pixel.png`;
+
+export const GREEN_BOX_IMAGE_URL = `${FIXTURES_PATH}/images/green_box.png`;
+export const RED_BOX_IMAGE_URL = `${FIXTURES_PATH}/images/red_box.png`;
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 6784b498c29..10143402acf 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,12 +1,12 @@
import Vue from 'vue';
-import authorTimeComponent from '~/vue_merge_request_widget/components/mr_widget_author_time.vue';
+import MrWidgetAuthorTime from '~/vue_merge_request_widget/components/mr_widget_author_time.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
-describe('MRWidgetAuthorTime', () => {
+describe('MrWidgetAuthorTime', () => {
let vm;
beforeEach(() => {
- const Component = Vue.extend(authorTimeComponent);
+ const Component = Vue.extend(MrWidgetAuthorTime);
vm = mountComponent(Component, {
actionText: 'Merged by',
@@ -34,7 +34,7 @@ describe('MRWidgetAuthorTime', () => {
});
it('renders provided time', () => {
- expect(vm.$el.querySelector('time').getAttribute('title')).toEqual('2017-03-23T23:02:00.807Z');
+ expect(vm.$el.querySelector('time').getAttribute('data-original-title')).toEqual('2017-03-23T23:02:00.807Z');
expect(vm.$el.querySelector('time').textContent.trim()).toEqual('12 hours ago');
});
});
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 9b9c9656979..3d36e46d863 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
@@ -12,6 +12,7 @@ describe('MRWidgetHeader', () => {
afterEach(() => {
vm.$destroy();
+ gon.relative_url_root = '';
});
describe('computed', () => {
@@ -145,7 +146,16 @@ describe('MRWidgetHeader', () => {
const button = vm.$el.querySelector('.js-web-ide');
expect(button.textContent.trim()).toEqual('Web IDE');
- expect(button.getAttribute('href')).toEqual('undefined/-/ide/projectabc');
+ expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
+ });
+
+ it('renders web ide button with relative URL', () => {
+ gon.relative_url_root = '/gitlab';
+
+ const button = vm.$el.querySelector('.js-web-ide');
+
+ expect(button.textContent.trim()).toEqual('Web IDE');
+ expect(button.getAttribute('href')).toEqual('/-/ide/projectabc');
});
it('renders download dropdown with links', () => {
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 a0a74648328..8de99fd3c96 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
@@ -6,6 +6,7 @@ import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('MRWidgetFailedToMerge', () => {
const dummyIntervalId = 1337;
let Component;
+ let mr;
let vm;
beforeEach(() => {
@@ -13,10 +14,11 @@ describe('MRWidgetFailedToMerge', () => {
spyOn(eventHub, '$emit');
spyOn(window, 'setInterval').and.returnValue(dummyIntervalId);
spyOn(window, 'clearInterval').and.stub();
+ mr = {
+ mergeError: 'Merge error happened',
+ };
vm = mountComponent(Component, {
- mr: {
- mergeError: 'Merge error happened.',
- },
+ mr,
});
});
@@ -44,6 +46,19 @@ describe('MRWidgetFailedToMerge', () => {
expect(vm.timerText).toEqual('Refreshing in a second to show the updated status...');
});
});
+
+ describe('mergeError', () => {
+ it('removes forced line breaks', done => {
+ mr.mergeError = 'contains<br />line breaks<br />';
+
+ Vue.nextTick()
+ .then(() => {
+ expect(vm.mergeError).toBe('contains line breaks');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
});
describe('created', () => {
@@ -103,7 +118,7 @@ describe('MRWidgetFailedToMerge', () => {
it('renders given error', () => {
expect(vm.$el.querySelector('.has-error-message').textContent.trim()).toEqual(
- 'Merge error happened..',
+ 'Merge error happened.',
);
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index adeea03481f..3e2fd71b5b8 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
@@ -186,7 +186,7 @@ describe('MRWidgetMerged', () => {
it('should use mergedEvent mergedAt as tooltip title', () => {
expect(
- vm.$el.querySelector('time').getAttribute('title'),
+ vm.$el.querySelector('time').getAttribute('data-original-title'),
).toBe('Jan 24, 2018 1:02pm GMT+0000');
});
});
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 30918428da2..6342ea00436 100644
--- a/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
+++ b/spec/javascripts/vue_mr_widget/mr_widget_options_spec.js
@@ -5,6 +5,7 @@ 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 { faviconDataUrl, overlayDataUrl, faviconWithOverlayDataUrl } from '../lib/utils/mock_data';
const returnPromise = data => new Promise((resolve) => {
resolve({
@@ -273,6 +274,7 @@ describe('mrWidgetOptions', () => {
beforeEach(() => {
const favicon = document.createElement('link');
favicon.setAttribute('id', 'favicon');
+ favicon.setAttribute('data-original-href', faviconDataUrl);
document.body.appendChild(favicon);
faviconElement = document.getElementById('favicon');
@@ -282,10 +284,13 @@ describe('mrWidgetOptions', () => {
document.body.removeChild(document.getElementById('favicon'));
});
- it('should call setFavicon method', () => {
- vm.setFaviconHelper();
-
- expect(faviconElement.getAttribute('href')).toEqual(vm.mr.ciStatusFaviconPath);
+ it('should call setFavicon method', (done) => {
+ vm.mr.ciStatusFaviconPath = overlayDataUrl;
+ vm.setFaviconHelper().then(() => {
+ expect(faviconElement.getAttribute('href')).toEqual(faviconWithOverlayDataUrl);
+ done();
+ })
+ .catch(done.fail);
});
it('should not call setFavicon when there is no ciStatusFaviconPath', () => {
diff --git a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
index 383f0cd29ea..e2c34508b0d 100644
--- a/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
+++ b/spec/javascripts/vue_shared/components/content_viewer/content_viewer_spec.js
@@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import contentViewer from '~/vue_shared/components/content_viewer/content_viewer.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { GREEN_BOX_IMAGE_URL } from 'spec/test_constants';
describe('ContentViewer', () => {
let vm;
@@ -41,12 +42,12 @@ describe('ContentViewer', () => {
it('renders image preview', done => {
createComponent({
- path: 'test.jpg',
+ path: GREEN_BOX_IMAGE_URL,
fileSize: 1024,
});
setTimeout(() => {
- expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe('test.jpg');
+ expect(vm.$el.querySelector('.image_file img').getAttribute('src')).toBe(GREEN_BOX_IMAGE_URL);
done();
});
@@ -59,9 +60,8 @@ describe('ContentViewer', () => {
});
setTimeout(() => {
- expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain(
- 'test.abc (1.00 KiB)',
- );
+ expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain('test.abc');
+ expect(vm.$el.querySelector('.file-info').textContent.trim()).toContain('(1.00 KiB)');
expect(vm.$el.querySelector('.btn.btn-default').textContent.trim()).toContain('Download');
done();
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
new file mode 100644
index 00000000000..71d9145bf22
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/diff_viewer/diff_viewer_spec.js
@@ -0,0 +1,70 @@
+import Vue from 'vue';
+import diffViewer from '~/vue_shared/components/diff_viewer/diff_viewer.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
+
+describe('DiffViewer', () => {
+ let vm;
+
+ function createComponent(props) {
+ const DiffViewer = Vue.extend(diffViewer);
+ vm = mountComponent(DiffViewer, props);
+ }
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders image diff', done => {
+ window.gon = {
+ relative_url_root: '',
+ };
+
+ createComponent({
+ diffMode: 'replaced',
+ newPath: GREEN_BOX_IMAGE_URL,
+ newSha: 'ABC',
+ oldPath: RED_BOX_IMAGE_URL,
+ oldSha: 'DEF',
+ projectPath: '',
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
+ `//raw/DEF/${RED_BOX_IMAGE_URL}`,
+ );
+
+ expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
+ `//raw/ABC/${GREEN_BOX_IMAGE_URL}`,
+ );
+
+ done();
+ });
+ });
+
+ it('renders fallback download diff display', done => {
+ createComponent({
+ diffMode: 'replaced',
+ newPath: 'test.abc',
+ newSha: 'ABC',
+ oldPath: 'testold.abc',
+ oldSha: 'DEF',
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.deleted .file-info').textContent.trim()).toContain(
+ 'testold.abc',
+ );
+ expect(vm.$el.querySelector('.deleted .btn.btn-default').textContent.trim()).toContain(
+ 'Download',
+ );
+
+ expect(vm.$el.querySelector('.added .file-info').textContent.trim()).toContain('test.abc');
+ expect(vm.$el.querySelector('.added .btn.btn-default').textContent.trim()).toContain(
+ 'Download',
+ );
+
+ done();
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
new file mode 100644
index 00000000000..b878286ae3f
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/diff_viewer/viewers/image_diff_viewer_spec.js
@@ -0,0 +1,185 @@
+import Vue from 'vue';
+import imageDiffViewer from '~/vue_shared/components/diff_viewer/viewers/image_diff_viewer.vue';
+import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
+
+describe('ImageDiffViewer', () => {
+ let vm;
+
+ function createComponent(props) {
+ const ImageDiffViewer = Vue.extend(imageDiffViewer);
+ vm = mountComponent(ImageDiffViewer, props);
+ }
+
+ const triggerEvent = (eventName, el = vm.$el, clientX = 0) => {
+ const event = document.createEvent('MouseEvents');
+ event.initMouseEvent(
+ eventName,
+ true,
+ true,
+ window,
+ 1,
+ clientX,
+ 0,
+ clientX,
+ 0,
+ false,
+ false,
+ false,
+ false,
+ 0,
+ null,
+ );
+
+ el.dispatchEvent(event);
+ };
+
+ const dragSlider = (sliderElement, dragPixel = 20) => {
+ triggerEvent('mousedown', sliderElement);
+ triggerEvent('mousemove', document.body, dragPixel);
+ triggerEvent('mouseup', document.body);
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders image diff for replaced', done => {
+ createComponent({
+ diffMode: 'replaced',
+ newPath: GREEN_BOX_IMAGE_URL,
+ oldPath: RED_BOX_IMAGE_URL,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
+ GREEN_BOX_IMAGE_URL,
+ );
+ expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
+ RED_BOX_IMAGE_URL,
+ );
+
+ expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('2-up');
+ expect(vm.$el.querySelector('.view-modes-menu li:nth-child(2)').textContent.trim()).toBe(
+ 'Swipe',
+ );
+ expect(vm.$el.querySelector('.view-modes-menu li:nth-child(3)').textContent.trim()).toBe(
+ 'Onion skin',
+ );
+
+ done();
+ });
+ });
+
+ it('renders image diff for new', done => {
+ createComponent({
+ diffMode: 'new',
+ newPath: GREEN_BOX_IMAGE_URL,
+ oldPath: '',
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.added .image_file img').getAttribute('src')).toBe(
+ GREEN_BOX_IMAGE_URL,
+ );
+
+ done();
+ });
+ });
+
+ it('renders image diff for deleted', done => {
+ createComponent({
+ diffMode: 'deleted',
+ newPath: '',
+ oldPath: RED_BOX_IMAGE_URL,
+ });
+
+ setTimeout(() => {
+ expect(vm.$el.querySelector('.deleted .image_file img').getAttribute('src')).toBe(
+ RED_BOX_IMAGE_URL,
+ );
+
+ done();
+ });
+ });
+
+ describe('swipeMode', () => {
+ beforeEach(done => {
+ createComponent({
+ diffMode: 'replaced',
+ newPath: GREEN_BOX_IMAGE_URL,
+ oldPath: RED_BOX_IMAGE_URL,
+ });
+
+ setTimeout(() => {
+ done();
+ });
+ });
+
+ it('switches to Swipe Mode', done => {
+ vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click();
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe('Swipe');
+ done();
+ });
+ });
+
+ it('drag handler is working', done => {
+ vm.$el.querySelector('.view-modes-menu li:nth-child(2)').click();
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('1px');
+ expect(vm.$el.querySelector('.top-handle')).not.toBeNull();
+
+ dragSlider(vm.$el.querySelector('.swipe-bar'), 40);
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.swipe-bar').style.left).toBe('-20px');
+ done();
+ });
+ });
+ });
+ });
+
+ describe('onionSkin', () => {
+ beforeEach(done => {
+ createComponent({
+ diffMode: 'replaced',
+ newPath: GREEN_BOX_IMAGE_URL,
+ oldPath: RED_BOX_IMAGE_URL,
+ });
+
+ setTimeout(() => {
+ done();
+ });
+ });
+
+ it('switches to Onion Skin Mode', done => {
+ vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click();
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.view-modes-menu li.active').textContent.trim()).toBe(
+ 'Onion skin',
+ );
+ done();
+ });
+ });
+
+ it('has working drag handler', done => {
+ vm.$el.querySelector('.view-modes-menu li:nth-child(3)').click();
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dragger').style.left).toBe('100px');
+
+ dragSlider(vm.$el.querySelector('.dragger'));
+
+ vm.$nextTick(() => {
+ expect(vm.$el.querySelector('.dragger').style.left).toBe('20px');
+ expect(vm.$el.querySelector('.added.frame').style.opacity).toBe('0.2');
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js b/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js
new file mode 100644
index 00000000000..2388660b0c2
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js
@@ -0,0 +1,13 @@
+import * as domUtils from '~/vue_shared/components/lib/utils/dom_utils';
+
+describe('domUtils', () => {
+ describe('pixeliseValue', () => {
+ it('should add px to a given Number', () => {
+ expect(domUtils.pixeliseValue(12)).toEqual('12px');
+ });
+
+ it('should not add px to 0', () => {
+ expect(domUtils.pixeliseValue(0)).toEqual('');
+ });
+ });
+});
diff --git a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
index c19de7b784a..41f957c4e00 100644
--- a/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
+++ b/spec/lib/banzai/filter/image_lazy_load_filter_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Banzai::Filter::ImageLazyLoadFilter, lib: true do
+describe Banzai::Filter::ImageLazyLoadFilter do
include FilterSpecHelper
def image(path)
diff --git a/spec/lib/banzai/filter/markdown_filter_spec.rb b/spec/lib/banzai/filter/markdown_filter_spec.rb
index 00c407d1b69..ab14d77d552 100644
--- a/spec/lib/banzai/filter/markdown_filter_spec.rb
+++ b/spec/lib/banzai/filter/markdown_filter_spec.rb
@@ -7,13 +7,13 @@ describe Banzai::Filter::MarkdownFilter do
it 'adds language to lang attribute when specified' do
result = filter("```html\nsome code\n```")
- expect(result).to start_with("\n<pre><code lang=\"html\">")
+ expect(result).to start_with("<pre><code lang=\"html\">")
end
it 'does not add language to lang attribute when not specified' do
result = filter("```\nsome code\n```")
- expect(result).to start_with("\n<pre><code>")
+ expect(result).to start_with("<pre><code>")
end
end
end
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index f8fa9b2d13d..91d4a60ba95 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper'
describe Banzai::Filter::MilestoneReferenceFilter do
include FilterSpecHelper
- let(:group) { create(:group, :public) }
+ let(:parent_group) { create(:group, :public) }
+ let(:group) { create(:group, :public, parent: parent_group) }
let(:project) { create(:project, :public, group: group) }
it 'requires project context' do
@@ -340,6 +341,13 @@ describe Banzai::Filter::MilestoneReferenceFilter do
expect(doc.css('a')).to be_empty
end
+
+ it 'supports parent group references', :nested_groups do
+ milestone.update!(group: parent_group)
+
+ doc = reference_filter("See #{reference}")
+ expect(doc.css('a').first.text).to eq(milestone.name)
+ end
end
context 'group context' do
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index 19028495f52..55490f37ac7 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -5,6 +5,13 @@ describe Gitlab::CurrentSettings do
stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')
end
+ shared_context 'with settings in cache' do
+ before do
+ create(:application_setting)
+ described_class.current_application_settings # warm the cache
+ end
+ end
+
describe '#current_application_settings', :use_clean_rails_memory_store_caching do
it 'allows keys to be called directly' do
db_settings = create(:application_setting,
@@ -31,16 +38,29 @@ describe Gitlab::CurrentSettings do
end
context 'with DB unavailable' do
- before do
- # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues
- # during the initialization phase of the test suite, so instead let's mock the internals of it
- allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false)
+ context 'and settings in cache' do
+ include_context 'with settings in cache'
+
+ it 'fetches the settings from cache without issuing any query' do
+ expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0)
+ end
end
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).not_to receive(:current)
+ context 'and no settings in cache' do
+ before do
+ # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(false)` causes issues
+ # during the initialization phase of the test suite, so instead let's mock the internals of it
+ allow(ActiveRecord::Base.connection).to receive(:active?).and_return(false)
+ expect(ApplicationSetting).not_to receive(:current)
+ end
- expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ end
+
+ it 'does not issue any query' do
+ expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0)
+ end
end
end
@@ -52,73 +72,86 @@ describe Gitlab::CurrentSettings do
ar_wrapped_defaults.slice(*::ApplicationSetting.defaults.keys)
end
- before do
- # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues
- # during the initialization phase of the test suite, so instead let's mock the internals of it
- allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true)
- allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true)
- end
+ context 'and settings in cache' do
+ include_context 'with settings in cache'
- it 'creates default ApplicationSettings if none are present' do
- settings = described_class.current_application_settings
-
- expect(settings).to be_a(ApplicationSetting)
- expect(settings).to be_persisted
- expect(settings).to have_attributes(settings_from_defaults)
+ it 'fetches the settings from cache' do
+ # For some reason, `allow(described_class).to receive(:connect_to_db?).and_return(true)` causes issues
+ # during the initialization phase of the test suite, so instead let's mock the internals of it
+ expect(ActiveRecord::Base.connection).not_to receive(:active?)
+ expect(ActiveRecord::Base.connection).not_to receive(:cached_table_exists?)
+ expect(ActiveRecord::Migrator).not_to receive(:needs_migration?)
+ expect(ActiveRecord::QueryRecorder.new { described_class.current_application_settings }.count).to eq(0)
+ end
end
- context 'with migrations pending' do
+ context 'and no settings in cache' do
before do
- expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
+ allow(ActiveRecord::Base.connection).to receive(:active?).and_return(true)
+ allow(ActiveRecord::Base.connection).to receive(:cached_table_exists?).with('application_settings').and_return(true)
end
- it 'returns an in-memory ApplicationSetting object' do
+ it 'creates default ApplicationSettings if none are present' do
settings = described_class.current_application_settings
- expect(settings).to be_a(Gitlab::FakeApplicationSettings)
- expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
- expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
+ expect(settings).to be_a(ApplicationSetting)
+ expect(settings).to be_persisted
+ expect(settings).to have_attributes(settings_from_defaults)
end
- it 'uses the existing database settings and falls back to defaults' do
- db_settings = create(:application_setting,
- home_page_url: 'http://mydomain.com',
- signup_enabled: false)
- settings = described_class.current_application_settings
- app_defaults = ApplicationSetting.last
-
- expect(settings).to be_a(Gitlab::FakeApplicationSettings)
- expect(settings.home_page_url).to eq(db_settings.home_page_url)
- expect(settings.signup_enabled?).to be_falsey
- expect(settings.signup_enabled).to be_falsey
-
- # Check that unspecified values use the defaults
- settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
- settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
+ context 'with migrations pending' do
+ before do
+ expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
+ end
+
+ it 'returns an in-memory ApplicationSetting object' do
+ settings = described_class.current_application_settings
+
+ expect(settings).to be_a(Gitlab::FakeApplicationSettings)
+ expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
+ expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
+ end
+
+ it 'uses the existing database settings and falls back to defaults' do
+ db_settings = create(:application_setting,
+ home_page_url: 'http://mydomain.com',
+ signup_enabled: false)
+ settings = described_class.current_application_settings
+ app_defaults = ApplicationSetting.last
+
+ expect(settings).to be_a(Gitlab::FakeApplicationSettings)
+ expect(settings.home_page_url).to eq(db_settings.home_page_url)
+ expect(settings.signup_enabled?).to be_falsey
+ expect(settings.signup_enabled).to be_falsey
+
+ # Check that unspecified values use the defaults
+ settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
+ settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
+ end
end
- end
- context 'when ApplicationSettings.current is present' do
- it 'returns the existing application settings' do
- expect(ApplicationSetting).to receive(:current).and_return(:current_settings)
+ context 'when ApplicationSettings.current is present' do
+ it 'returns the existing application settings' do
+ expect(ApplicationSetting).to receive(:current).and_return(:current_settings)
- expect(described_class.current_application_settings).to eq(:current_settings)
+ expect(described_class.current_application_settings).to eq(:current_settings)
+ end
end
- end
- context 'when the application_settings table does not exists' do
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid)
+ context 'when the application_settings table does not exists' do
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::StatementInvalid)
- expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ end
end
- end
- context 'when the application_settings table is not fully migrated' do
- it 'returns an in-memory ApplicationSetting object' do
- expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError)
+ context 'when the application_settings table is not fully migrated' do
+ it 'returns an in-memory ApplicationSetting object' do
+ expect(ApplicationSetting).to receive(:create_from_defaults).and_raise(ActiveRecord::UnknownAttributeError)
- expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ expect(described_class.current_application_settings).to be_a(Gitlab::FakeApplicationSettings)
+ end
end
end
end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index f0e83ccfc7a..5dfbb8e71f8 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -79,7 +79,9 @@ describe Gitlab::Diff::File do
let(:diffs) { commit.diffs }
before do
- info_dir_path = File.join(project.repository.path_to_repo, 'info')
+ info_dir_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ File.join(project.repository.path_to_repo, 'info')
+ end
FileUtils.mkdir(info_dir_path) unless File.exist?(info_dir_path)
File.write(File.join(info_dir_path, 'attributes'), "*.md -diff\n")
diff --git a/spec/lib/gitlab/favicon_spec.rb b/spec/lib/gitlab/favicon_spec.rb
new file mode 100644
index 00000000000..08c4a474217
--- /dev/null
+++ b/spec/lib/gitlab/favicon_spec.rb
@@ -0,0 +1,52 @@
+require 'rails_helper'
+
+RSpec.describe Gitlab::Favicon, :request_store do
+ describe '.main' do
+ it 'defaults to favicon.png' do
+ allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('production'))
+ expect(described_class.main).to match_asset_path '/assets/favicon.png'
+ end
+
+ it 'has blue favicon for development' do
+ allow(Rails).to receive(:env).and_return(ActiveSupport::StringInquirer.new('development'))
+ expect(described_class.main).to match_asset_path '/assets/favicon-blue.png'
+ end
+
+ it 'has yellow favicon for canary' do
+ stub_env('CANARY', 'true')
+ expect(described_class.main).to match_asset_path 'favicon-yellow.png'
+ end
+
+ it 'uses the custom favicon if a favicon appearance is present' do
+ create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
+ expect(described_class.main).to match %r{/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png}
+ end
+ end
+
+ describe '.status_overlay' do
+ subject { described_class.status_overlay('favicon_status_created') }
+
+ it 'returns the overlay for the status' do
+ expect(subject).to match_asset_path '/assets/ci_favicons/favicon_status_created.png'
+ end
+ end
+
+ describe '.available_status_names' do
+ subject { described_class.available_status_names }
+
+ it 'returns the available status names' do
+ expect(subject).to eq %w(
+ favicon_status_canceled
+ favicon_status_created
+ favicon_status_failed
+ favicon_status_manual
+ favicon_status_not_found
+ favicon_status_pending
+ favicon_status_running
+ favicon_status_skipped
+ favicon_status_success
+ favicon_status_warning
+ )
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/blame_spec.rb b/spec/lib/gitlab/git/blame_spec.rb
index 793228701cf..ba790b717ae 100644
--- a/spec/lib/gitlab/git/blame_spec.rb
+++ b/spec/lib/gitlab/git/blame_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::Git::Blame, seed_helper: true do
Gitlab::Git::Blame.new(repository, SeedRepo::Commit::ID, "CONTRIBUTING.md")
end
- shared_examples 'blaming a file' do
+ describe 'blaming a file' do
context "each count" do
it do
data = []
@@ -68,12 +68,4 @@ describe Gitlab::Git::Blame, seed_helper: true do
end
end
end
-
- context 'when Gitaly blame feature is enabled' do
- it_behaves_like 'blaming a file'
- end
-
- context 'when Gitaly blame feature is disabled', :skip_gitaly_mock do
- it_behaves_like 'blaming a file'
- end
end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 94eaf86ef80..6015086f002 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -149,7 +149,9 @@ describe Gitlab::Git::Blob, seed_helper: true do
it 'limits the size of a large file' do
blob_size = Gitlab::Git::Blob::MAX_DATA_DISPLAY_SIZE + 1
buffer = Array.new(blob_size, 0)
- rugged_blob = Rugged::Blob.from_buffer(repository.rugged, buffer.join(''))
+ rugged_blob = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Rugged::Blob.from_buffer(repository.rugged, buffer.join(''))
+ end
blob = Gitlab::Git::Blob.raw(repository, rugged_blob)
expect(blob.size).to eq(blob_size)
@@ -164,7 +166,9 @@ describe Gitlab::Git::Blob, seed_helper: true do
context 'when sha references a tree' do
it 'returns nil' do
- tree = repository.rugged.rev_parse('master^{tree}')
+ tree = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.rev_parse('master^{tree}')
+ end
blob = Gitlab::Git::Blob.raw(repository, tree.oid)
@@ -278,7 +282,11 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
describe '.batch_lfs_pointers' do
- let(:tree_object) { repository.rugged.rev_parse('master^{tree}') }
+ let(:tree_object) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.rev_parse('master^{tree}')
+ end
+ end
let(:non_lfs_blob) do
Gitlab::Git::Blob.find(
diff --git a/spec/lib/gitlab/git/branch_spec.rb b/spec/lib/gitlab/git/branch_spec.rb
index a19155ed5b0..ec1a684cfbc 100644
--- a/spec/lib/gitlab/git/branch_spec.rb
+++ b/spec/lib/gitlab/git/branch_spec.rb
@@ -69,7 +69,9 @@ describe Gitlab::Git::Branch, seed_helper: true do
Gitlab::Git.committer_hash(email: user.email, name: user.name)
end
let(:params) do
- parents = [repository.rugged.head.target]
+ parents = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ [repository.rugged.head.target]
+ end
tree = parents.first.tree
{
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 89be8a1b7f2..ae69a362dda 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -4,12 +4,15 @@ describe Gitlab::Git::Commit, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '') }
let(:commit) { described_class.find(repository, SeedRepo::Commit::ID) }
let(:rugged_commit) do
- repository.rugged.lookup(SeedRepo::Commit::ID)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.lookup(SeedRepo::Commit::ID)
+ end
end
-
describe "Commit info" do
before do
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ end
@committer = {
email: 'mike@smith.com',
@@ -58,7 +61,9 @@ describe Gitlab::Git::Commit, seed_helper: true do
after do
# Erase the new commit so other tests get the original repo
- repo = Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ repo = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '').rugged
+ end
repo.references.update("refs/heads/master", SeedRepo::LastCommit::ID)
end
end
@@ -115,7 +120,9 @@ describe Gitlab::Git::Commit, seed_helper: true do
describe '.find' do
it "should return first head commit if without params" do
expect(described_class.last(repository).id).to eq(
- repository.rugged.head.target.oid
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.head.target.oid
+ end
)
end
@@ -414,6 +421,16 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
+ describe '#batch_by_oid' do
+ context 'when oids is empty' do
+ it 'makes no Gitaly request' do
+ expect(Gitlab::GitalyClient).not_to receive(:call)
+
+ described_class.batch_by_oid(repository, [])
+ end
+ end
+ end
+
shared_examples 'extracting commit signature' do
context 'when the commit is signed' do
let(:commit_id) { '0b4bc9a49b562e85de7cc9e834518ea6828729b9' }
diff --git a/spec/lib/gitlab/git/diff_spec.rb b/spec/lib/gitlab/git/diff_spec.rb
index 4a7b06003fc..3bb0b5be15b 100644
--- a/spec/lib/gitlab/git/diff_spec.rb
+++ b/spec/lib/gitlab/git/diff_spec.rb
@@ -27,8 +27,10 @@ EOT
too_large: false
}
- @rugged_diff = repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
- [".gitmodules"]).patches.first
+ @rugged_diff = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repository.rugged.diff("5937ac0a7beb003549fc5fd26fc247adbce4a52e^", "5937ac0a7beb003549fc5fd26fc247adbce4a52e", paths:
+ [".gitmodules"]).patches.first
+ end
end
describe '.new' do
diff --git a/spec/lib/gitlab/git/gitlab_projects_spec.rb b/spec/lib/gitlab/git/gitlab_projects_spec.rb
index 8b715d717c1..f5d8503c30c 100644
--- a/spec/lib/gitlab/git/gitlab_projects_spec.rb
+++ b/spec/lib/gitlab/git/gitlab_projects_spec.rb
@@ -5,6 +5,13 @@ describe Gitlab::Git::GitlabProjects do
TestEnv.clean_test_path
end
+ around do |example|
+ # TODO move this spec to gitaly-ruby. GitlabProjects is not used in gitlab-ce
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
let(:project) { create(:project, :repository) }
if $VERBOSE
@@ -190,36 +197,30 @@ describe Gitlab::Git::GitlabProjects do
end
end
- context 'when Gitaly import_repository feature is enabled' do
- it_behaves_like 'importing repository'
- end
+ describe 'logging' do
+ it 'imports a repo' do
+ message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>."
+ expect(logger).to receive(:info).with(message)
- context 'when Gitaly import_repository feature is disabled', :disable_gitaly do
- describe 'logging' do
- it 'imports a repo' do
- message = "Importing project from <#{import_url}> to <#{tmp_repo_path}>."
- expect(logger).to receive(:info).with(message)
-
- subject
- end
+ subject
end
+ end
- context 'timeout' do
- it 'does not import a repo' do
- stub_spawn_timeout(cmd, timeout, nil)
+ context 'timeout' do
+ it 'does not import a repo' do
+ stub_spawn_timeout(cmd, timeout, nil)
- message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed."
- expect(logger).to receive(:error).with(message)
+ message = "Importing project from <#{import_url}> to <#{tmp_repo_path}> failed."
+ expect(logger).to receive(:error).with(message)
- is_expected.to be_falsy
+ is_expected.to be_falsy
- expect(gl_projects.output).to eq("Timed out\n")
- expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
- end
+ expect(gl_projects.output).to eq("Timed out\n")
+ expect(File.exist?(File.join(tmp_repo_path, 'HEAD'))).to be_falsy
end
-
- it_behaves_like 'importing repository'
end
+
+ it_behaves_like 'importing repository'
end
describe '#fork_repository' do
@@ -232,9 +233,6 @@ describe Gitlab::Git::GitlabProjects do
before do
FileUtils.mkdir_p(dest_repos_path)
-
- # Undo spec_helper stub that deletes hooks
- allow_any_instance_of(described_class).to receive(:fork_repository).and_call_original
end
after do
@@ -258,51 +256,45 @@ describe Gitlab::Git::GitlabProjects do
end
end
- context 'when Gitaly fork_repository feature is enabled' do
- it_behaves_like 'forking a repository'
- end
-
- context 'when Gitaly fork_repository feature is disabled', :disable_gitaly do
- it_behaves_like 'forking a repository'
+ it_behaves_like 'forking a repository'
- # We seem to be stuck to having only one working Gitaly storage in tests, changing
- # that is not very straight-forward so I'm leaving this test here for now till
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed.
- context 'different storages' do
- let(:dest_repos) { 'alternative' }
- let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), dest_repos) }
+ # We seem to be stuck to having only one working Gitaly storage in tests, changing
+ # that is not very straight-forward so I'm leaving this test here for now till
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/41393 is fixed.
+ context 'different storages' do
+ let(:dest_repos) { 'alternative' }
+ let(:dest_repos_path) { File.join(File.dirname(tmp_repos_path), dest_repos) }
- before do
- stub_storage_settings(dest_repos => { 'path' => dest_repos_path })
- end
+ before do
+ stub_storage_settings(dest_repos => { 'path' => dest_repos_path })
+ end
- it 'forks the repo' do
- is_expected.to be_truthy
+ it 'forks the repo' do
+ is_expected.to be_truthy
- expect(File.exist?(dest_repo)).to be_truthy
- expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
- expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
- end
+ expect(File.exist?(dest_repo)).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'pre-receive'))).to be_truthy
+ expect(File.exist?(File.join(dest_repo, 'hooks', 'post-receive'))).to be_truthy
end
+ end
- describe 'log messages' do
- describe 'successful fork' do
- it do
- message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>."
- expect(logger).to receive(:info).with(message)
+ describe 'log messages' do
+ describe 'successful fork' do
+ it do
+ message = "Forking repository from <#{tmp_repo_path}> to <#{dest_repo}>."
+ expect(logger).to receive(:info).with(message)
- subject
- end
+ subject
end
+ end
- describe 'failed fork due existing destination' do
- it do
- FileUtils.mkdir_p(dest_repo)
- message = "fork-repository failed: destination repository <#{dest_repo}> already exists."
- expect(logger).to receive(:error).with(message)
+ describe 'failed fork due existing destination' do
+ it do
+ FileUtils.mkdir_p(dest_repo)
+ message = "fork-repository failed: destination repository <#{dest_repo}> already exists."
+ expect(logger).to receive(:error).with(message)
- subject
- end
+ subject
end
end
end
diff --git a/spec/lib/gitlab/git/hook_spec.rb b/spec/lib/gitlab/git/hook_spec.rb
index 2fe1f5603ce..a45c8510b15 100644
--- a/spec/lib/gitlab/git/hook_spec.rb
+++ b/spec/lib/gitlab/git/hook_spec.rb
@@ -8,25 +8,39 @@ describe Gitlab::Git::Hook do
allow_any_instance_of(described_class).to receive(:trigger).and_call_original
end
+ around do |example|
+ # TODO move hook tests to gitaly-ruby. Hook will disappear from gitlab-ce
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
describe "#trigger" do
- let(:project) { create(:project, :repository) }
+ set(:project) { create(:project, :repository) }
let(:repository) { project.repository.raw_repository }
let(:repo_path) { repository.path }
+ let(:hooks_dir) { File.join(repo_path, 'hooks') }
let(:user) { create(:user) }
let(:gl_id) { Gitlab::GlId.gl_id(user) }
let(:gl_username) { user.username }
def create_hook(name)
- FileUtils.mkdir_p(File.join(repo_path, 'hooks'))
- File.open(File.join(repo_path, 'hooks', name), 'w', 0755) do |f|
- f.write('exit 0')
+ FileUtils.mkdir_p(hooks_dir)
+ hook_path = File.join(hooks_dir, name)
+ File.open(hook_path, 'w', 0755) do |f|
+ f.write(<<~HOOK)
+ #!/bin/sh
+ exit 0
+ HOOK
end
end
def create_failing_hook(name)
- FileUtils.mkdir_p(File.join(repo_path, 'hooks'))
- File.open(File.join(repo_path, 'hooks', name), 'w', 0755) do |f|
- f.write(<<-HOOK)
+ FileUtils.mkdir_p(hooks_dir)
+ hook_path = File.join(hooks_dir, name)
+ File.open(hook_path, 'w', 0755) do |f|
+ f.write(<<~HOOK)
+ #!/bin/sh
echo 'regular message from the hook'
echo 'error message from the hook' 1>&2
echo 'error message from the hook line 2' 1>&2
@@ -38,7 +52,7 @@ describe Gitlab::Git::Hook do
['pre-receive', 'post-receive', 'update'].each do |hook_name|
context "when triggering a #{hook_name} hook" do
context "when the hook is successful" do
- let(:hook_path) { File.join(repo_path, 'hooks', hook_name) }
+ let(:hook_path) { File.join(hooks_dir, hook_name) }
let(:gl_repository) { Gitlab::GlRepository.gl_repository(project, false) }
let(:env) do
{
@@ -76,7 +90,7 @@ describe Gitlab::Git::Hook do
status, errors = hook.trigger(gl_id, gl_username, blank, blank, ref)
expect(status).to be false
- expect(errors).to eq("error message from the hook<br>error message from the hook line 2<br>")
+ expect(errors).to eq("error message from the hook\nerror message from the hook line 2\n")
end
end
end
diff --git a/spec/lib/gitlab/git/hooks_service_spec.rb b/spec/lib/gitlab/git/hooks_service_spec.rb
index 3ed3feb4c74..9337aa39e13 100644
--- a/spec/lib/gitlab/git/hooks_service_spec.rb
+++ b/spec/lib/gitlab/git/hooks_service_spec.rb
@@ -26,24 +26,24 @@ describe Gitlab::Git::HooksService, seed_helper: true do
context 'when pre-receive hook failed' do
it 'does not call post-receive hook' do
- expect(service).to receive(:run_hook).with('pre-receive').and_return([false, ''])
+ expect(service).to receive(:run_hook).with('pre-receive').and_return([false, 'hello world'])
expect(service).not_to receive(:run_hook).with('post-receive')
expect do
service.execute(user, repository, blankrev, newrev, ref)
- end.to raise_error(Gitlab::Git::HooksService::PreReceiveError)
+ end.to raise_error(Gitlab::Git::PreReceiveError, 'hello world')
end
end
context 'when update hook failed' do
it 'does not call post-receive hook' do
expect(service).to receive(:run_hook).with('pre-receive').and_return([true, nil])
- expect(service).to receive(:run_hook).with('update').and_return([false, ''])
+ expect(service).to receive(:run_hook).with('update').and_return([false, 'hello world'])
expect(service).not_to receive(:run_hook).with('post-receive')
expect do
service.execute(user, repository, blankrev, newrev, ref)
- end.to raise_error(Gitlab::Git::HooksService::PreReceiveError)
+ end.to raise_error(Gitlab::Git::PreReceiveError, 'hello world')
end
end
end
diff --git a/spec/lib/gitlab/git/index_spec.rb b/spec/lib/gitlab/git/index_spec.rb
index 73fbc6a6afa..16e6bd35449 100644
--- a/spec/lib/gitlab/git/index_spec.rb
+++ b/spec/lib/gitlab/git/index_spec.rb
@@ -8,6 +8,13 @@ describe Gitlab::Git::Index, seed_helper: true do
index.read_tree(repository.lookup('master').tree)
end
+ around do |example|
+ # TODO move these specs to gitaly-ruby. The Index class will disappear from gitlab-ce
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ example.run
+ end
+ end
+
describe '#create' do
let(:options) do
{
diff --git a/spec/lib/gitlab/git/pre_receive_error_spec.rb b/spec/lib/gitlab/git/pre_receive_error_spec.rb
new file mode 100644
index 00000000000..1b8be62dec6
--- /dev/null
+++ b/spec/lib/gitlab/git/pre_receive_error_spec.rb
@@ -0,0 +1,9 @@
+require 'spec_helper'
+
+describe Gitlab::Git::PreReceiveError do
+ it 'makes its message HTML-friendly' do
+ ex = described_class.new("hello\nworld\n")
+
+ expect(ex.message).to eq('hello<br>world<br>')
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 1744db1b17e..5bae99101e6 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -77,17 +77,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#root_ref' do
- context 'with gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
- end
-
- it 'calls #discover_default_branch' do
- expect(repository).to receive(:discover_default_branch)
- repository.root_ref
- end
- end
-
it 'returns UTF-8' do
expect(repository.root_ref).to be_utf8
end
@@ -153,46 +142,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- describe "#discover_default_branch" do
- let(:master) { 'master' }
- let(:feature) { 'feature' }
- let(:feature2) { 'feature2' }
-
- around do |example|
- # discover_default_branch will be moved to gitaly-ruby
- Gitlab::GitalyClient::StorageSettings.allow_disk_access do
- example.run
- end
- end
-
- it "returns 'master' when master exists" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master])
- expect(repository.discover_default_branch).to eq('master')
- end
-
- it "returns non-master when master exists but default branch is set to something else" do
- File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/feature')
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, master])
- expect(repository.discover_default_branch).to eq('feature')
- File.write(File.join(repository_path, 'HEAD'), 'ref: refs/heads/master')
- end
-
- it "returns a non-master branch when only one exists" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature])
- expect(repository.discover_default_branch).to eq('feature')
- end
-
- it "returns a non-master branch when more than one exists and master does not" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([feature, feature2])
- expect(repository.discover_default_branch).to eq('feature')
- end
-
- it "returns nil when no branch exists" do
- expect(repository).to receive(:branch_names).at_least(:once).and_return([])
- expect(repository.discover_default_branch).to be_nil
- end
- end
-
describe '#branch_names' do
subject { repository.branch_names }
@@ -476,7 +425,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
describe '#has_local_branches?' do
- shared_examples 'check for local branches' do
+ context 'check for local branches' do
it { expect(repository.has_local_branches?).to eq(true) }
context 'mutable' do
@@ -510,14 +459,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
end
-
- context 'with gitaly' do
- it_behaves_like 'check for local branches'
- end
-
- context 'without gitaly', :skip_gitaly_mock do
- it_behaves_like 'check for local branches'
- end
end
describe "#delete_branch" do
@@ -1395,24 +1336,6 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
- # With Gitaly enabled, Gitaly just doesn't return deleted branches.
- context 'with deleted branch with Gitaly disabled' do
- before do
- allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_return(false)
- end
-
- it 'returns no results' do
- ref = double()
- allow(ref).to receive(:name) { 'bad-branch' }
- allow(ref).to receive(:target) { raise Rugged::ReferenceError }
- branches = double()
- allow(branches).to receive(:each) { [ref].each }
- allow(repository_rugged).to receive(:branches) { branches }
-
- expect(subject).to be_empty
- end
- end
-
it_behaves_like 'wrapping gRPC errors', Gitlab::GitalyClient::RefService, :branches
end
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
index 70e90659b0f..95dc47e2a00 100644
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ b/spec/lib/gitlab/git/rev_list_spec.rb
@@ -88,7 +88,7 @@ describe Gitlab::Git::RevList do
context '#all_objects' do
it 'fetches list of all pushed objects using rev-list' do
- stub_popen_rev_list('--all', '--objects', '--filter=blob:limit=200', output: "sha1\nsha2")
+ stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
expect { |b| rev_list.all_objects(&b) }.to yield_with_args(%w[sha1 sha2])
end
diff --git a/spec/lib/gitlab/git/wiki_spec.rb b/spec/lib/gitlab/git/wiki_spec.rb
index 722d697c28e..35b06b14620 100644
--- a/spec/lib/gitlab/git/wiki_spec.rb
+++ b/spec/lib/gitlab/git/wiki_spec.rb
@@ -25,6 +25,22 @@ describe Gitlab::Git::Wiki do
end
end
+ describe '#delete_page', :skip_gitaly_mock do
+ after do
+ destroy_page('page1')
+ end
+
+ it 'only removes the page with the same path' do
+ create_page('page1', 'content')
+ create_page('*', 'content')
+
+ subject.delete_page('*', commit_details('whatever'))
+
+ expect(subject.pages.count).to eq 1
+ expect(subject.pages.first.title).to eq 'page1'
+ end
+ end
+
def create_page(name, content)
subject.write_page(name, :markdown, content, commit_details(name))
end
diff --git a/spec/lib/gitlab/git_access_wiki_spec.rb b/spec/lib/gitlab/git_access_wiki_spec.rb
index 730ede99fc9..9c6c9fe13bf 100644
--- a/spec/lib/gitlab/git_access_wiki_spec.rb
+++ b/spec/lib/gitlab/git_access_wiki_spec.rb
@@ -52,7 +52,9 @@ describe Gitlab::GitAccessWiki do
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)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ FileUtils.rm_rf(wiki_repo.path)
+ end
# Sanity check for rm_rf
expect(wiki_repo.exists?).to eq(false)
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index 9fbdd73ee0e..9709f1f5646 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -48,7 +48,7 @@ describe Gitlab::GitalyClient::OperationService do
.and_return(response)
expect { subject }.to raise_error(
- Gitlab::Git::HooksService::PreReceiveError, "something failed")
+ Gitlab::Git::PreReceiveError, "something failed")
end
end
end
@@ -85,7 +85,7 @@ describe Gitlab::GitalyClient::OperationService do
.and_return(response)
expect { subject }.to raise_error(
- Gitlab::Git::HooksService::PreReceiveError, "something failed")
+ Gitlab::Git::PreReceiveError, "something failed")
end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index 6686b7ce0b5..3422a1e82fc 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -276,5 +276,17 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
expect(diff.merge_request_diff_commits.exists?).to eq(true)
end
+
+ context 'when the merge request exists' do
+ it 'creates the merge request diffs if they do not yet exist' do
+ mr, _ = importer.create_merge_request
+
+ mr.merge_request_diffs.delete_all
+
+ importer.insert_git_data(mr, true)
+
+ expect(mr.merge_request_diffs.exists?).to eq(true)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/sequential_importer_spec.rb b/spec/lib/gitlab/github_import/sequential_importer_spec.rb
index 6089b0b751f..05d3243f806 100644
--- a/spec/lib/gitlab/github_import/sequential_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/sequential_importer_spec.rb
@@ -30,7 +30,6 @@ describe Gitlab::GithubImport::SequentialImporter do
expect(instance).to receive(:execute)
end
- expect(repository).to receive(:after_import)
expect(importer.execute).to eq(true)
end
end
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
new file mode 100644
index 00000000000..813ae43b4d3
--- /dev/null
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -0,0 +1,75 @@
+require 'spec_helper'
+
+describe Gitlab::HashedStorage::Migrator do
+ describe '#bulk_schedule' do
+ it 'schedules job to StorageMigratorWorker' do
+ Sidekiq::Testing.fake! do
+ expect { subject.bulk_schedule(1, 5) }.to change(StorageMigratorWorker.jobs, :size).by(1)
+ end
+ end
+ end
+
+ describe '#bulk_migrate' do
+ let(:projects) { create_list(:project, 2, :legacy_storage) }
+ let(:ids) { projects.map(&:id) }
+
+ it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do
+ Sidekiq::Testing.fake! do
+ expect { subject.bulk_migrate(ids.min, ids.max) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(2)
+ end
+ end
+
+ it 'sets projects as read only' do
+ allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice
+ subject.bulk_migrate(ids.min, ids.max)
+
+ projects.each do |project|
+ expect(project.reload.repository_read_only?).to be_truthy
+ end
+ end
+
+ it 'rescues and log exceptions' do
+ allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError)
+ expect { subject.bulk_migrate(ids.min, ids.max) }.not_to raise_error
+ end
+
+ it 'delegates each project in specified range to #migrate' do
+ projects.each do |project|
+ expect(subject).to receive(:migrate).with(project)
+ end
+
+ subject.bulk_migrate(ids.min, ids.max)
+ end
+ end
+
+ describe '#migrate' do
+ let(:project) { create(:project, :legacy_storage, :empty_repo) }
+
+ it 'enqueues job to ProjectMigrateHashedStorageWorker' do
+ Sidekiq::Testing.fake! do
+ expect { subject.migrate(project) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(1)
+ end
+ end
+
+ it 'rescues and log exceptions' do
+ allow(project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError)
+
+ expect { subject.migrate(project) }.not_to raise_error
+ end
+
+ it 'sets project as read only' do
+ allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async)
+ subject.migrate(project)
+
+ expect(project.reload.repository_read_only?).to be_truthy
+ end
+
+ it 'migrate project' do
+ Sidekiq::Testing.inline! do
+ subject.migrate(project)
+ end
+
+ expect(project.reload.hashed_storage?(:attachments)).to be_truthy
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_export/avatar_saver_spec.rb b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
index f40d4bc2d08..2223f163177 100644
--- a/spec/lib/gitlab/import_export/avatar_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/avatar_saver_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::AvatarSaver do
let(:shared) { project.import_export_shared }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
- let(:project_with_avatar) { create(:project, avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")) }
+ let(:project_with_avatar) { create(:project, avatar: fixture_file_upload("spec/fixtures/dk.png", "image/png")) }
let(:project) { create(:project) }
before do
diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
index b793636c4d6..68eaa70e6b6 100644
--- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
+++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
@@ -19,7 +19,9 @@ describe Gitlab::ImportExport::MergeRequestParser do
end
after do
- FileUtils.rm_rf(project.repository.path_to_repo)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ FileUtils.rm_rf(project.repository.path_to_repo)
+ end
end
it 'has a source branch' do
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 5b289ceb3b2..0a1e3eb83d3 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -170,7 +170,7 @@ MergeRequest:
- last_edited_by_id
- head_pipeline_id
- discussion_locked
-- allow_collaboration
+- allow_maintainer_to_push
MergeRequestDiff:
- id
- state
@@ -540,6 +540,7 @@ ProjectAutoDevops:
- id
- enabled
- domain
+- deploy_strategy
- project_id
- created_at
- updated_at
diff --git a/spec/lib/gitlab/import_export/uploads_saver_spec.rb b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
index 1304d8fabfc..095687fa89d 100644
--- a/spec/lib/gitlab/import_export/uploads_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/uploads_saver_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::ImportExport::UploadsSaver do
describe 'bundle a project Git repo' do
let(:export_path) { "#{Dir.tmpdir}/uploads_saver_spec" }
- let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+ let(:file) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
let(:shared) { project.import_export_shared }
before do
diff --git a/spec/lib/gitlab/quick_actions/extractor_spec.rb b/spec/lib/gitlab/quick_actions/extractor_spec.rb
index f7c288f2393..0166f6c2ee0 100644
--- a/spec/lib/gitlab/quick_actions/extractor_spec.rb
+++ b/spec/lib/gitlab/quick_actions/extractor_spec.rb
@@ -182,6 +182,14 @@ describe Gitlab::QuickActions::Extractor do
expect(msg).to eq "hello\nworld"
end
+ it 'extracts command case insensitive' do
+ msg = %(hello\n/PoWer @user.name %9.10 ~"bar baz.2"\nworld)
+ msg, commands = extractor.extract_commands(msg)
+
+ expect(commands).to eq [['power', '@user.name %9.10 ~"bar baz.2"']]
+ expect(msg).to eq "hello\nworld"
+ end
+
it 'does not extract noop commands' do
msg = %(hello\nworld\n/reopen\n/noop_command)
msg, commands = extractor.extract_commands(msg)
@@ -206,6 +214,14 @@ describe Gitlab::QuickActions::Extractor do
expect(msg).to eq "hello\nworld\nthis is great? SHRUG"
end
+ it 'extracts and performs substitution commands case insensitive' do
+ msg = %(hello\nworld\n/reOpen\n/sHRuG this is great?)
+ msg, commands = extractor.extract_commands(msg)
+
+ expect(commands).to eq [['reopen'], ['shrug', 'this is great?']]
+ expect(msg).to eq "hello\nworld\nthis is great? SHRUG"
+ end
+
it 'extracts and performs substitution commands with comments' do
msg = %(hello\nworld\n/reopen\n/substitution wow this is a thing.)
msg, commands = extractor.extract_commands(msg)
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 14eae22a2ec..155e1663298 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -498,16 +498,34 @@ describe Gitlab::Shell do
)
end
- it 'returns true when the command succeeds' do
- expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { true }
+ context 'with gitaly' do
+ it 'returns true when the command succeeds' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository)
+ .with(repository.raw_repository) { :gitaly_response_object }
+
+ is_expected.to be_truthy
+ end
+
+ it 'return false when the command fails' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:fork_repository)
+ .with(repository.raw_repository) { raise GRPC::BadStatus, 'bla' }
- is_expected.to be_truthy
+ is_expected.to be_falsy
+ end
end
- it 'return false when the command fails' do
- expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { false }
+ context 'without gitaly', :disable_gitaly do
+ it 'returns true when the command succeeds' do
+ expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { true }
- is_expected.to be_falsy
+ is_expected.to be_truthy
+ end
+
+ it 'return false when the command fails' do
+ expect(gitlab_projects).to receive(:fork_repository).with('nfs-file05', 'fork/path.git') { false }
+
+ is_expected.to be_falsy
+ end
end
end
@@ -662,21 +680,43 @@ describe Gitlab::Shell do
describe '#import_repository' do
let(:import_url) { 'https://gitlab.com/gitlab-org/gitlab-ce.git' }
- it 'returns true when the command succeeds' do
- expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
+ context 'with gitaly' do
+ it 'returns true when the command succeeds' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository).with(import_url)
- result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
+ result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
- expect(result).to be_truthy
+ expect(result).to be_truthy
+ end
+
+ it 'raises an exception when the command fails' do
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:import_repository)
+ .with(import_url) { raise GRPC::BadStatus, 'bla' }
+ expect_any_instance_of(Gitlab::Shell::GitalyGitlabProjects).to receive(:output) { 'error'}
+
+ expect do
+ gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
+ end.to raise_error(Gitlab::Shell::Error, "error")
+ end
end
- it 'raises an exception when the command fails' do
- allow(gitlab_projects).to receive(:output) { 'error' }
- expect(gitlab_projects).to receive(:import_project) { false }
+ context 'without gitaly', :disable_gitaly do
+ it 'returns true when the command succeeds' do
+ expect(gitlab_projects).to receive(:import_project).with(import_url, timeout) { true }
- expect do
- gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
- end.to raise_error(Gitlab::Shell::Error, "error")
+ result = gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
+
+ expect(result).to be_truthy
+ end
+
+ it 'raises an exception when the command fails' do
+ allow(gitlab_projects).to receive(:output) { 'error' }
+ expect(gitlab_projects).to receive(:import_project) { false }
+
+ expect do
+ gitlab_shell.import_repository(project.repository_storage, project.disk_path, import_url)
+ end.to raise_error(Gitlab::Shell::Error, "error")
+ end
end
end
end
diff --git a/spec/lib/gitlab/sql/cte_spec.rb b/spec/lib/gitlab/sql/cte_spec.rb
new file mode 100644
index 00000000000..d6763c7b2e1
--- /dev/null
+++ b/spec/lib/gitlab/sql/cte_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Gitlab::SQL::CTE, :postgresql do
+ describe '#to_arel' do
+ it 'generates an Arel relation for the CTE body' do
+ relation = User.where(id: 1)
+ cte = described_class.new(:cte_name, relation)
+ sql = cte.to_arel.to_sql
+ name = ActiveRecord::Base.connection.quote_table_name(:cte_name)
+
+ sql1 = ActiveRecord::Base.connection.unprepared_statement do
+ relation.except(:order).to_sql
+ end
+
+ expect(sql).to eq("#{name} AS (#{sql1})")
+ end
+ end
+
+ describe '#alias_to' do
+ it 'returns an alias for the CTE' do
+ cte = described_class.new(:cte_name, nil)
+ table = Arel::Table.new(:kittens)
+
+ source_name = ActiveRecord::Base.connection.quote_table_name(:cte_name)
+ alias_name = ActiveRecord::Base.connection.quote_table_name(:kittens)
+
+ expect(cte.alias_to(table).to_sql).to eq("#{source_name} AS #{alias_name}")
+ end
+ end
+
+ describe '#apply_to' do
+ it 'applies a CTE to an ActiveRecord::Relation' do
+ user = create(:user)
+ cte = described_class.new(:cte_name, User.where(id: user.id))
+
+ relation = cte.apply_to(User.all)
+
+ expect(relation.to_sql).to match(/WITH .+cte_name/)
+ expect(relation.to_a).to eq(User.where(id: user.id).to_a)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/themes_spec.rb b/spec/lib/gitlab/themes_spec.rb
index ecacea6bb35..a8213988f70 100644
--- a/spec/lib/gitlab/themes_spec.rb
+++ b/spec/lib/gitlab/themes_spec.rb
@@ -5,9 +5,9 @@ describe Gitlab::Themes, lib: true do
it 'returns a space-separated list of class names' do
css = described_class.body_classes
- expect(css).to include('ui_indigo')
- expect(css).to include(' ui_dark ')
- expect(css).to include(' ui_blue')
+ expect(css).to include('ui-indigo')
+ expect(css).to include('ui-dark')
+ expect(css).to include('ui-blue')
end
end
diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb
index 81dbbb962dd..6f5f9938eca 100644
--- a/spec/lib/gitlab/url_blocker_spec.rb
+++ b/spec/lib/gitlab/url_blocker_spec.rb
@@ -58,20 +58,6 @@ describe Gitlab::UrlBlocker do
end
end
- it 'returns true for a non-alphanumeric username' do
- stub_resolv
-
- aggregate_failures do
- expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a')
-
- # The leading character here is a Unicode "soft hyphen"
- expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a')
-
- # Unicode alphanumerics are allowed
- expect(described_class).not_to be_blocked_url('ssh://ğitlab@example.com/a')
- end
- end
-
it 'returns true for invalid URL' do
expect(described_class.blocked_url?('http://:8080')).to be true
end
@@ -120,6 +106,38 @@ describe Gitlab::UrlBlocker do
allow(Addrinfo).to receive(:getaddrinfo).and_call_original
end
end
+
+ context 'when enforce_user is' do
+ before do
+ stub_resolv
+ end
+
+ context 'false (default)' do
+ it 'does not block urls with a non-alphanumeric username' do
+ expect(described_class).not_to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a')
+
+ # The leading character here is a Unicode "soft hyphen"
+ expect(described_class).not_to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a')
+
+ # Unicode alphanumerics are allowed
+ expect(described_class).not_to be_blocked_url('ssh://ğitlab@example.com/a')
+ end
+ end
+
+ context 'true' do
+ it 'blocks urls with a non-alphanumeric username' do
+ aggregate_failures do
+ expect(described_class).to be_blocked_url('ssh://-oProxyCommand=whoami@example.com/a', enforce_user: true)
+
+ # The leading character here is a Unicode "soft hyphen"
+ expect(described_class).to be_blocked_url('ssh://­oProxyCommand=whoami@example.com/a', enforce_user: true)
+
+ # Unicode alphanumerics are allowed
+ expect(described_class).not_to be_blocked_url('ssh://ğitlab@example.com/a', enforce_user: true)
+ end
+ end
+ end
+ end
end
# Resolv does not support resolving UTF-8 domain names
diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb
index b81749cf428..9f495a5d50b 100644
--- a/spec/lib/gitlab/url_builder_spec.rb
+++ b/spec/lib/gitlab/url_builder_spec.rb
@@ -22,6 +22,31 @@ describe Gitlab::UrlBuilder do
end
end
+ context 'when passing a Milestone' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, :public, namespace: group) }
+
+ context 'belonging to a project' do
+ it 'returns a proper URL' do
+ milestone = create(:milestone, project: project)
+
+ url = described_class.build(milestone)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/#{milestone.project.full_path}/milestones/#{milestone.iid}"
+ end
+ end
+
+ context 'belonging to a group' do
+ it 'returns a proper URL' do
+ milestone = create(:milestone, group: group)
+
+ url = described_class.build(milestone)
+
+ expect(url).to eq "#{Settings.gitlab['url']}/groups/#{milestone.group.full_path}/-/milestones/#{milestone.iid}"
+ end
+ end
+ end
+
context 'when passing a MergeRequest' do
it 'returns a proper URL' do
merge_request = build_stubbed(:merge_request, iid: 42)
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index a716e6f5434..22d921716aa 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -32,6 +32,7 @@ describe Gitlab::UsageData do
mattermost_enabled
edition
version
+ installation_type
uuid
hostname
signup
@@ -156,6 +157,7 @@ describe Gitlab::UsageData do
it "gathers license data" do
expect(subject[:uuid]).to eq(Gitlab::CurrentSettings.uuid)
expect(subject[:version]).to eq(Gitlab::VERSION)
+ expect(subject[:installation_type]).to eq(Gitlab::INSTALLATION_TYPE)
expect(subject[:active_user_count]).to eq(User.active.count)
expect(subject[:recorded_at]).to be_a(Time)
end
diff --git a/spec/lib/gitlab/verify/job_artifacts_spec.rb b/spec/lib/gitlab/verify/job_artifacts_spec.rb
index ec490bdfde2..6e916a56564 100644
--- a/spec/lib/gitlab/verify/job_artifacts_spec.rb
+++ b/spec/lib/gitlab/verify/job_artifacts_spec.rb
@@ -21,15 +21,38 @@ describe Gitlab::Verify::JobArtifacts do
FileUtils.rm_f(artifact.file.path)
expect(failures.keys).to contain_exactly(artifact)
- expect(failure).to be_a(Errno::ENOENT)
- expect(failure.to_s).to include(artifact.file.path)
+ expect(failure).to include('No such file or directory')
+ expect(failure).to include(artifact.file.path)
end
it 'fails artifacts with a mismatched checksum' do
File.truncate(artifact.file.path, 0)
expect(failures.keys).to contain_exactly(artifact)
- expect(failure.to_s).to include('Checksum mismatch')
+ expect(failure).to include('Checksum mismatch')
+ end
+
+ context 'with remote files' do
+ let(:file) { double(:file) }
+
+ before do
+ stub_artifacts_object_storage
+ artifact.update!(file_store: ObjectStorage::Store::REMOTE)
+ expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
+ end
+
+ it 'passes artifacts in object storage that exist' do
+ expect(file).to receive(:exists?).and_return(true)
+
+ expect(failures).to eq({})
+ end
+
+ it 'fails artifacts in object storage that do not exist' do
+ expect(file).to receive(:exists?).and_return(false)
+
+ expect(failures.keys).to contain_exactly(artifact)
+ expect(failure).to include('Remote object does not exist')
+ end
end
end
end
diff --git a/spec/lib/gitlab/verify/lfs_objects_spec.rb b/spec/lib/gitlab/verify/lfs_objects_spec.rb
index 0f890e2c7ce..2feaedd6f14 100644
--- a/spec/lib/gitlab/verify/lfs_objects_spec.rb
+++ b/spec/lib/gitlab/verify/lfs_objects_spec.rb
@@ -21,30 +21,37 @@ describe Gitlab::Verify::LfsObjects do
FileUtils.rm_f(lfs_object.file.path)
expect(failures.keys).to contain_exactly(lfs_object)
- expect(failure).to be_a(Errno::ENOENT)
- expect(failure.to_s).to include(lfs_object.file.path)
+ expect(failure).to include('No such file or directory')
+ expect(failure).to include(lfs_object.file.path)
end
it 'fails LFS objects with a mismatched oid' do
File.truncate(lfs_object.file.path, 0)
expect(failures.keys).to contain_exactly(lfs_object)
- expect(failure.to_s).to include('Checksum mismatch')
+ expect(failure).to include('Checksum mismatch')
end
context 'with remote files' do
+ let(:file) { double(:file) }
+
before do
stub_lfs_object_storage
+ lfs_object.update!(file_store: ObjectStorage::Store::REMOTE)
+ expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
- it 'skips LFS objects in object storage' do
- local_failure = create(:lfs_object)
- create(:lfs_object, :object_storage)
+ it 'passes LFS objects in object storage that exist' do
+ expect(file).to receive(:exists?).and_return(true)
+
+ expect(failures).to eq({})
+ end
- failures = {}
- described_class.new(batch_size: 10).run_batches { |_, failed| failures.merge!(failed) }
+ it 'fails LFS objects in object storage that do not exist' do
+ expect(file).to receive(:exists?).and_return(false)
- expect(failures.keys).to contain_exactly(local_failure)
+ expect(failures.keys).to contain_exactly(lfs_object)
+ expect(failure).to include('Remote object does not exist')
end
end
end
diff --git a/spec/lib/gitlab/verify/uploads_spec.rb b/spec/lib/gitlab/verify/uploads_spec.rb
index 85768308edc..296866d3319 100644
--- a/spec/lib/gitlab/verify/uploads_spec.rb
+++ b/spec/lib/gitlab/verify/uploads_spec.rb
@@ -23,37 +23,44 @@ describe Gitlab::Verify::Uploads do
FileUtils.rm_f(upload.absolute_path)
expect(failures.keys).to contain_exactly(upload)
- expect(failure).to be_a(Errno::ENOENT)
- expect(failure.to_s).to include(upload.absolute_path)
+ expect(failure).to include('No such file or directory')
+ expect(failure).to include(upload.absolute_path)
end
it 'fails uploads with a mismatched checksum' do
upload.update!(checksum: 'something incorrect')
expect(failures.keys).to contain_exactly(upload)
- expect(failure.to_s).to include('Checksum mismatch')
+ expect(failure).to include('Checksum mismatch')
end
it 'fails uploads with a missing precalculated checksum' do
upload.update!(checksum: '')
expect(failures.keys).to contain_exactly(upload)
- expect(failure.to_s).to include('Checksum missing')
+ expect(failure).to include('Checksum missing')
end
context 'with remote files' do
+ let(:file) { double(:file) }
+
before do
stub_uploads_object_storage(AvatarUploader)
+ upload.update!(store: ObjectStorage::Store::REMOTE)
+ expect(CarrierWave::Storage::Fog::File).to receive(:new).and_return(file)
end
- it 'skips uploads in object storage' do
- local_failure = create(:upload)
- create(:upload, :object_storage)
+ it 'passes uploads in object storage that exist' do
+ expect(file).to receive(:exists?).and_return(true)
+
+ expect(failures).to eq({})
+ end
- failures = {}
- described_class.new(batch_size: 10).run_batches { |_, failed| failures.merge!(failed) }
+ it 'fails uploads in object storage that do not exist' do
+ expect(file).to receive(:exists?).and_return(false)
- expect(failures.keys).to contain_exactly(local_failure)
+ expect(failures.keys).to contain_exactly(upload)
+ expect(failure).to include('Remote object does not exist')
end
end
end
diff --git a/spec/lib/microsoft_teams/notifier_spec.rb b/spec/lib/microsoft_teams/notifier_spec.rb
index 3035693812f..c9756544bd6 100644
--- a/spec/lib/microsoft_teams/notifier_spec.rb
+++ b/spec/lib/microsoft_teams/notifier_spec.rb
@@ -8,7 +8,7 @@ describe MicrosoftTeams::Notifier do
let(:options) do
{
title: 'JohnDoe4/project2',
- pretext: '[[JohnDoe4/project2](http://localhost/namespace2/gitlabhq)] Issue [#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1) opened by user6',
+ summary: '[[JohnDoe4/project2](http://localhost/namespace2/gitlabhq)] Issue [#1 Awesome issue](http://localhost/namespace2/gitlabhq/issues/1) opened by user6',
activity: {
title: 'Issue opened by user6',
subtitle: 'in [JohnDoe4/project2](http://localhost/namespace2/gitlabhq)',
diff --git a/spec/migrations/active_record/schema_spec.rb b/spec/migrations/active_record/schema_spec.rb
index e132529d8d8..9d35b3cd642 100644
--- a/spec/migrations/active_record/schema_spec.rb
+++ b/spec/migrations/active_record/schema_spec.rb
@@ -5,7 +5,11 @@ require 'spec_helper'
describe ActiveRecord::Schema do
let(:latest_migration_timestamp) do
- migrations = Dir[Rails.root.join('db', 'migrate', '*'), Rails.root.join('db', 'post_migrate', '*')]
+ migrations_paths = %w[db ee/db]
+ .product(%w[migrate post_migrate])
+ .map { |path| Rails.root.join(*path, '*') }
+
+ migrations = Dir[*migrations_paths]
migrations.map { |migration| File.basename(migration).split('_').first.to_i }.max
end
diff --git a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
index 4ee1d255fbd..ac34efa4f9d 100644
--- a/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
+++ b/spec/migrations/migrate_process_commit_worker_jobs_spec.rb
@@ -6,7 +6,11 @@ require Rails.root.join('db', 'migrate', '20161124141322_migrate_process_commit_
describe MigrateProcessCommitWorkerJobs do
let(:project) { create(:project, :legacy_storage, :repository) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
let(:user) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let(:commit) { project.commit.raw.rugged_commit }
+ let(:commit) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.commit.raw.rugged_commit
+ end
+ end
describe 'Project' do
describe 'find_including_path' 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 560409f08de..5f5ba426d69 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
@@ -49,10 +49,14 @@ describe TurnNestedGroupsIntoRegularGroupsForMysql do
end
it 'renames the repository of any projects' do
- expect(updated_project.repository.path)
+ repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ updated_project.repository.path
+ end
+
+ expect(repo_path)
.to end_with("#{parent_group.name}-#{child_group.name}/#{updated_project.path}.git")
- expect(File.directory?(updated_project.repository.path)).to eq(true)
+ expect(File.directory?(repo_path)).to eq(true)
end
it 'creates a redirect route for renamed projects' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 0a0d7d3fea9..51b9b518117 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -1548,7 +1548,9 @@ describe Ci::Build do
let(:predefined_variables) do
[
{ key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
+ { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true },
{ key: 'CI_JOB_ID', value: build.id.to_s, public: true },
+ { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true },
{ key: 'CI_JOB_TOKEN', value: build.token, public: false },
{ key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
{ key: 'CI_BUILD_TOKEN', value: build.token, public: false },
@@ -2171,6 +2173,7 @@ describe Ci::Build do
it 'does not return prohibited variables' do
keys = %w[CI_JOB_ID
+ CI_JOB_URL
CI_JOB_TOKEN
CI_BUILD_ID
CI_BUILD_TOKEN
diff --git a/spec/models/ci/job_artifact_spec.rb b/spec/models/ci/job_artifact_spec.rb
index a3e119cbc27..efddd4e7662 100644
--- a/spec/models/ci/job_artifact_spec.rb
+++ b/spec/models/ci/job_artifact_spec.rb
@@ -76,12 +76,12 @@ describe Ci::JobArtifact do
context 'updating the artifact file' do
it 'updates the artifact size' do
- artifact.update!(file: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png')))
+ artifact.update!(file: fixture_file_upload('spec/fixtures/dk.png'))
expect(artifact.size).to eq(1062)
end
it 'updates the project statistics' do
- expect { artifact.update!(file: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
+ expect { artifact.update!(file: fixture_file_upload('spec/fixtures/dk.png')) }
.to change { artifact.project.statistics.reload.build_artifacts_size }
.by(1062 - 106365)
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 2bae98dcbb8..a41657b53b7 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -194,7 +194,7 @@ describe Ci::Pipeline, :mailer do
it 'does contains persisted variables' do
keys = subject.map { |variable| variable[:key] }
- expect(keys).to eq %w[CI_PIPELINE_ID]
+ expect(keys).to eq %w[CI_PIPELINE_ID CI_PIPELINE_URL]
end
end
end
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index b3797c1fb46..2d75422ee68 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -156,7 +156,7 @@ describe CacheMarkdownField do
end
it { expect(thing.foo_html).to eq(updated_html) }
- it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION) }
+ it { expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION) }
end
describe '#cached_html_up_to_date?' do
@@ -234,7 +234,7 @@ describe CacheMarkdownField do
it 'returns default version when version is nil' do
thing.cached_markdown_version = nil
- is_expected.to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ is_expected.to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
@@ -261,7 +261,7 @@ describe CacheMarkdownField do
thing.cached_markdown_version = nil
thing.refresh_markdown_cache
- expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
@@ -346,7 +346,7 @@ describe CacheMarkdownField do
expect(thing.foo_html).to eq(updated_html)
expect(thing.baz_html).to eq(updated_html)
- expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
@@ -366,7 +366,7 @@ describe CacheMarkdownField do
expect(thing.foo_html).to eq(updated_html)
expect(thing.baz_html).to eq(updated_html)
- expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_REDCARPET_VERSION)
+ expect(thing.cached_markdown_version).to eq(CacheMarkdownField::CACHE_COMMONMARK_VERSION)
end
end
end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index f83b52e8975..9fe1186a8c9 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -67,6 +67,30 @@ describe Group do
end
end
+ describe '#notification_settings', :nested_groups do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:sub_group) { create(:group, parent_id: group.id) }
+
+ before do
+ group.add_developer(user)
+ sub_group.add_developer(user)
+ end
+
+ it 'also gets notification settings from parent groups' do
+ expect(sub_group.notification_settings.size).to eq(2)
+ expect(sub_group.notification_settings).to include(group.notification_settings.first)
+ end
+
+ context 'when sub group is deleted' do
+ it 'does not delete parent notification settings' do
+ expect do
+ sub_group.destroy
+ end.to change { NotificationSetting.count }.by(-1)
+ end
+ end
+ end
+
describe '#visibility_level_allowed_by_parent' do
let(:parent) { create(:group, :internal) }
let(:sub_group) { build(:group, parent_id: parent.id) }
@@ -240,7 +264,7 @@ describe Group do
it "is false if avatar is html page" do
group.update_attribute(:avatar, 'uploads/avatar.html')
- expect(group.avatar_type).to eq(["file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff"])
+ expect(group.avatar_type).to eq(["file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico"])
end
end
diff --git a/spec/models/notification_recipient_spec.rb b/spec/models/notification_recipient_spec.rb
index eda0e1da835..13fe47799ed 100644
--- a/spec/models/notification_recipient_spec.rb
+++ b/spec/models/notification_recipient_spec.rb
@@ -13,4 +13,48 @@ describe NotificationRecipient do
expect(recipient.has_access?).to be_falsy
end
+
+ context '#notification_setting' do
+ context 'for child groups', :nested_groups do
+ let!(:moved_group) { create(:group) }
+ let(:group) { create(:group) }
+ let(:sub_group_1) { create(:group, parent: group) }
+ let(:sub_group_2) { create(:group, parent: sub_group_1) }
+ let(:project) { create(:project, namespace: moved_group) }
+
+ before do
+ sub_group_2.add_owner(user)
+ moved_group.add_owner(user)
+ Groups::TransferService.new(moved_group, user).execute(sub_group_2)
+
+ moved_group.reload
+ end
+
+ context 'when notification setting is global' do
+ before do
+ user.notification_settings_for(group).global!
+ user.notification_settings_for(sub_group_1).mention!
+ user.notification_settings_for(sub_group_2).global!
+ user.notification_settings_for(moved_group).global!
+ end
+
+ it 'considers notification setting from the first parent without global setting' do
+ expect(subject.notification_setting.source).to eq(sub_group_1)
+ end
+ end
+
+ context 'when notification setting is not global' do
+ before do
+ user.notification_settings_for(group).global!
+ user.notification_settings_for(sub_group_1).mention!
+ user.notification_settings_for(sub_group_2).watch!
+ user.notification_settings_for(moved_group).disabled!
+ end
+
+ it 'considers notification setting from lowest group member in hierarchy' do
+ expect(subject.notification_setting.source).to eq(moved_group)
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/project_auto_devops_spec.rb b/spec/models/project_auto_devops_spec.rb
index 7545c0797e9..749b2094787 100644
--- a/spec/models/project_auto_devops_spec.rb
+++ b/spec/models/project_auto_devops_spec.rb
@@ -5,6 +5,8 @@ describe ProjectAutoDevops do
it { is_expected.to belong_to(:project) }
+ it { is_expected.to define_enum_for(:deploy_strategy) }
+
it { is_expected.to respond_to(:created_at) }
it { is_expected.to respond_to(:updated_at) }
@@ -67,8 +69,127 @@ describe ProjectAutoDevops do
end
end
+ context 'when deploy_strategy is manual' do
+ let(:domain) { 'example.com' }
+
+ before do
+ auto_devops.deploy_strategy = 'manual'
+ end
+
+ it do
+ expect(auto_devops.predefined_variables.map { |var| var[:key] })
+ .to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
+ end
+ end
+
+ context 'when deploy_strategy is continuous' do
+ let(:domain) { 'example.com' }
+
+ before do
+ auto_devops.deploy_strategy = 'continuous'
+ end
+
+ it do
+ expect(auto_devops.predefined_variables.map { |var| var[:key] })
+ .not_to include("STAGING_ENABLED", "INCREMENTAL_ROLLOUT_ENABLED")
+ end
+ end
+
def domain_variable
{ key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true }
end
end
+
+ describe '#set_gitlab_deploy_token' do
+ let(:auto_devops) { build(:project_auto_devops, project: project) }
+
+ context 'when the project is public' do
+ let(:project) { create(:project, :repository, :public) }
+
+ it 'should not create a gitlab deploy token' do
+ expect do
+ auto_devops.save
+ end.not_to change { DeployToken.count }
+ end
+ end
+
+ context 'when the project is internal' do
+ let(:project) { create(:project, :repository, :internal) }
+
+ it 'should create a gitlab deploy token' do
+ expect do
+ auto_devops.save
+ end.to change { DeployToken.count }.by(1)
+ end
+ end
+
+ context 'when the project is private' do
+ let(:project) { create(:project, :repository, :private) }
+
+ it 'should create a gitlab deploy token' do
+ expect do
+ auto_devops.save
+ end.to change { DeployToken.count }.by(1)
+ end
+ end
+
+ context 'when autodevops is enabled at project level' do
+ let(:project) { create(:project, :repository, :internal) }
+ let(:auto_devops) { build(:project_auto_devops, project: project) }
+
+ it 'should create a deploy token' do
+ expect do
+ auto_devops.save
+ end.to change { DeployToken.count }.by(1)
+ end
+ end
+
+ context 'when autodevops is enabled at instancel level' do
+ let(:project) { create(:project, :repository, :internal) }
+ let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) }
+
+ it 'should create a deploy token' do
+ allow(Gitlab::CurrentSettings).to receive(:auto_devops_enabled?).and_return(true)
+
+ expect do
+ auto_devops.save
+ end.to change { DeployToken.count }.by(1)
+ end
+ end
+
+ context 'when autodevops is disabled' do
+ let(:project) { create(:project, :repository, :internal) }
+ let(:auto_devops) { build(:project_auto_devops, :disabled, project: project) }
+
+ it 'should not create a deploy token' do
+ expect do
+ auto_devops.save
+ end.not_to change { DeployToken.count }
+ end
+ end
+
+ context 'when the project already has an active gitlab-deploy-token' do
+ let(:project) { create(:project, :repository, :internal) }
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, projects: [project]) }
+ let(:auto_devops) { build(:project_auto_devops, project: project) }
+
+ it 'should not create a deploy token' do
+ expect do
+ auto_devops.save
+ end.not_to change { DeployToken.count }
+ end
+ end
+
+ context 'when the project already has a revoked gitlab-deploy-token' do
+ let(:project) { create(:project, :repository, :internal) }
+ let!(:deploy_token) { create(:deploy_token, :gitlab_deploy_token, :expired, projects: [project]) }
+ let(:auto_devops) { build(:project_auto_devops, project: project) }
+
+ it 'should not create a deploy token' do
+ expect do
+ auto_devops.save
+ end.not_to change { DeployToken.count }
+ end
+ end
+ end
end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 54ef0be67ff..b9f1c7dd5df 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe JiraService do
include Gitlab::Routing
+ include AssetsHelpers
describe '#options' do
let(:service) do
@@ -164,6 +165,8 @@ describe JiraService do
it "creates Remote Link reference in JIRA for comment" do
@jira_service.close_issue(merge_request, ExternalIssue.new("JIRA-123", project))
+ favicon_path = "http://localhost/assets/#{find_asset('favicon.png').digest_path}"
+
# Creates comment
expect(WebMock).to have_requested(:post, @comment_url)
# Creates Remote Link in JIRA issue fields
@@ -173,7 +176,7 @@ describe JiraService do
object: {
url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{merge_request.diff_head_sha}",
title: "GitLab: Solved by commit #{merge_request.diff_head_sha}.",
- icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" },
+ icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: true }
}
)
@@ -464,4 +467,18 @@ describe JiraService do
end
end
end
+
+ describe 'favicon urls', :request_store do
+ it 'includes the standard favicon' do
+ props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
+ expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/assets/favicon(?:-\h+).png$}
+ end
+
+ it 'includes returns the custom favicon' do
+ create :appearance, favicon: fixture_file_upload('spec/fixtures/dk.png')
+
+ props = described_class.new.send(:build_remote_link_props, url: 'http://example.com', title: 'title')
+ expect(props[:object][:icon][:url16x16]).to match %r{^http://localhost/uploads/-/system/appearance/favicon/\d+/favicon_main_dk.png$}
+ end
+ end
end
diff --git a/spec/models/project_services/microsoft_teams_service_spec.rb b/spec/models/project_services/microsoft_teams_service_spec.rb
index 8d9ee96227f..3351c6280b4 100644
--- a/spec/models/project_services/microsoft_teams_service_spec.rb
+++ b/spec/models/project_services/microsoft_teams_service_spec.rb
@@ -225,10 +225,15 @@ describe MicrosoftTeamsService do
it 'calls Microsoft Teams API for pipeline events' do
data = Gitlab::DataBuilder::Pipeline.build(pipeline)
+ data[:markdown] = true
chat_service.execute(data)
- expect(WebMock).to have_requested(:post, webhook_url).once
+ message = ChatMessage::PipelineMessage.new(data)
+
+ expect(WebMock).to have_requested(:post, webhook_url)
+ .with(body: hash_including({ summary: message.summary }))
+ .once
end
end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index fe9d64c0e3b..bc9cce6b0c3 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -238,20 +238,27 @@ describe Project do
expect(project2.import_data).to be_nil
end
- it "does not allow blocked import_url localhost" do
+ it "does not allow import_url pointing to localhost" do
project2 = build(:project, import_url: 'http://localhost:9000/t.git')
expect(project2).to be_invalid
expect(project2.errors[:import_url].first).to include('Requests to localhost are not allowed')
end
- it "does not allow blocked import_url port" do
+ it "does not allow import_url with invalid ports" do
project2 = build(:project, import_url: 'http://github.com:25/t.git')
expect(project2).to be_invalid
expect(project2.errors[:import_url].first).to include('Only allowed ports are 22, 80, 443')
end
+ it "does not allow import_url with invalid user" do
+ project2 = build(:project, import_url: 'http://$user:password@github.com/t.git')
+
+ expect(project2).to be_invalid
+ expect(project2.errors[:import_url].first).to include('Username needs to start with an alphanumeric character')
+ end
+
describe 'project pending deletion' do
let!(:project_pending_deletion) do
create(:project,
@@ -960,7 +967,7 @@ describe Project do
it 'is false if avatar is html page' do
project.update_attribute(:avatar, 'uploads/avatar.html')
- expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff'])
+ expect(project.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico'])
end
end
@@ -1693,6 +1700,31 @@ describe Project do
end
end
+ describe '#human_import_status_name' do
+ context 'when import_state exists' do
+ it 'returns the humanized status name' do
+ project = create(:project)
+ create(:import_state, :started, project: project)
+
+ expect(project.human_import_status_name).to eq("started")
+ end
+ end
+
+ context 'when import_state was not created yet' do
+ let(:project) { create(:project, :import_started) }
+
+ it 'ensures import_state is created and returns humanized status name' do
+ expect do
+ project.human_import_status_name
+ end.to change { ProjectImportState.count }.from(0).to(1)
+ end
+
+ it 'returns humanized status name' do
+ expect(project.human_import_status_name).to eq("started")
+ end
+ end
+ end
+
describe 'Project import job' do
let(:project) { create(:project, import_url: generate(:url)) }
@@ -1701,7 +1733,11 @@ describe Project do
.with(project.repository_storage, project.disk_path, project.import_url)
.and_return(true)
- expect_any_instance_of(Repository).to receive(:after_import)
+ # Works around https://github.com/rspec/rspec-mocks/issues/910
+ allow(described_class).to receive(:find).with(project.id).and_return(project)
+ expect(project.repository).to receive(:after_import)
+ .and_call_original
+ expect(project.wiki.repository).to receive(:after_import)
.and_call_original
end
@@ -2907,7 +2943,7 @@ describe Project do
project.rename_repo
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+ expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
end
end
@@ -3068,7 +3104,7 @@ describe Project do
it 'updates project full path in .git/config' do
project.rename_repo
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq(project.full_path)
+ expect(rugged_config['gitlab.fullpath']).to eq(project.full_path)
end
end
@@ -3366,10 +3402,11 @@ describe Project do
end
describe '#after_import' do
- let(:project) { build(:project) }
+ let(:project) { create(:project) }
it 'runs the correct hooks' do
expect(project.repository).to receive(:after_import)
+ expect(project.wiki.repository).to receive(:after_import)
expect(project).to receive(:import_finish)
expect(project).to receive(:update_project_counter_caches)
expect(project).to receive(:remove_import_jid)
@@ -3488,13 +3525,13 @@ describe Project do
it 'writes full path in .git/config when key is missing' do
project.write_repository_config
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ expect(rugged_config['gitlab.fullpath']).to eq project.full_path
end
it 'updates full path in .git/config when key is present' do
project.write_repository_config(gl_full_path: 'old/path')
- expect { project.write_repository_config }.to change { project.repository.rugged.config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
+ expect { project.write_repository_config }.to change { rugged_config['gitlab.fullpath'] }.from('old/path').to(project.full_path)
end
it 'does not raise an error with an empty repository' do
@@ -3626,6 +3663,11 @@ describe Project do
.to be_truthy
end
+ it 'allows access when there are merge requests open but no branch name is given' do
+ expect(project.branch_allows_collaboration?(user, nil))
+ .to be_truthy
+ end
+
it 'does not allow guest users access' do
guest = create(:user)
target_project.add_guest(guest)
@@ -3775,4 +3817,10 @@ describe Project do
let(:uploader_class) { AttachmentUploader }
end
end
+
+ def rugged_config
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged.config
+ end
+ end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index f1142832f1a..a3c20b3b3c1 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -188,7 +188,11 @@ describe ProjectWiki do
before do
subject.wiki # Make sure the wiki repo exists
- BareRepoOperations.new(subject.repository.path_to_repo).commit_file(image, 'image.png')
+ repo_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ subject.repository.path_to_repo
+ end
+
+ BareRepoOperations.new(repo_path).commit_file(image, 'image.png')
end
it 'returns the latest version of the file if it exists' do
diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb
index 1d94abe4195..3597b080021 100644
--- a/spec/models/remote_mirror_spec.rb
+++ b/spec/models/remote_mirror_spec.rb
@@ -15,6 +15,13 @@ describe RemoteMirror do
expect(remote_mirror).not_to be_valid
end
+
+ it 'does not allow url with an invalid user' do
+ remote_mirror = build(:remote_mirror, url: 'http://$user:password@invalid.invalid')
+
+ expect(remote_mirror).to be_invalid
+ expect(remote_mirror.errors[:url].first).to include('Username needs to start with an alphanumeric character')
+ end
end
end
@@ -67,7 +74,9 @@ describe RemoteMirror do
mirror.update_attribute(:url, 'http://foo:baz@test.com')
- config = repo.raw_repository.rugged.config
+ config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ repo.raw_repository.rugged.config
+ end
expect(config["remote.#{mirror.remote_name}.url"]).to eq('http://foo:baz@test.com')
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 7c0a1cd967c..b6df048d4ca 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1195,7 +1195,7 @@ describe Repository do
Gitlab::Git::OperationService.new(git_user, repository.raw_repository).with_branch('feature') do
new_rev
end
- end.to raise_error(Gitlab::Git::HooksService::PreReceiveError)
+ end.to raise_error(Gitlab::Git::PreReceiveError)
end
end
@@ -1938,13 +1938,13 @@ describe Repository do
context 'when pre hooks failed' do
before do
allow_any_instance_of(Gitlab::GitalyClient::OperationService)
- .to receive(:user_delete_branch).and_raise(Gitlab::Git::HooksService::PreReceiveError)
+ .to receive(:user_delete_branch).and_raise(Gitlab::Git::PreReceiveError)
end
it 'gets an error and does not delete the branch' do
expect do
repository.rm_branch(user, 'feature')
- end.to raise_error(Gitlab::Git::HooksService::PreReceiveError)
+ end.to raise_error(Gitlab::Git::PreReceiveError)
expect(repository.find_branch('feature')).not_to be_nil
end
@@ -1980,7 +1980,7 @@ describe Repository do
expect do
repository.rm_branch(user, 'feature')
- end.to raise_error(Gitlab::Git::HooksService::PreReceiveError)
+ end.to raise_error(Gitlab::Git::PreReceiveError)
end
it 'does not delete the branch' do
@@ -1988,7 +1988,7 @@ describe Repository do
expect do
repository.rm_branch(user, 'feature')
- end.to raise_error(Gitlab::Git::HooksService::PreReceiveError)
+ end.to raise_error(Gitlab::Git::PreReceiveError)
expect(repository.find_branch('feature')).not_to be_nil
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 09dfeae6377..097144d04ce 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1260,7 +1260,7 @@ describe User do
it 'is false if avatar is html page' do
user.update_attribute(:avatar, 'uploads/avatar.html')
- expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff'])
+ expect(user.avatar_type).to eq(['file format is not supported. Please try one of the following supported formats: png, jpg, jpeg, gif, bmp, tiff, ico'])
end
end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 8ad19e3f0f5..e73d1a252f5 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -18,14 +18,14 @@ describe API::Commits do
describe 'GET /projects/:id/repository/commits' do
let(:route) { "/projects/#{project_id}/repository/commits" }
- shared_examples_for 'project commits' do
+ shared_examples_for 'project commits' do |schema: 'public_api/v4/commits'|
it "returns project commits" do
commit = project.repository.commit
get api(route, current_user)
expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/commits')
+ expect(response).to match_response_schema(schema)
expect(json_response.first['id']).to eq(commit.id)
expect(json_response.first['committer_name']).to eq(commit.committer_name)
expect(json_response.first['committer_email']).to eq(commit.committer_email)
@@ -161,6 +161,23 @@ describe API::Commits do
end
end
+ context 'with_stats optional parameter' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it_behaves_like 'project commits', schema: 'public_api/v4/commits_with_stats' do
+ let(:route) { "/projects/#{project_id}/repository/commits?with_stats=true" }
+
+ it 'include commits details' do
+ commit = project.repository.commit
+ get api(route, current_user)
+
+ expect(json_response.first['stats']['additions']).to eq(commit.stats.additions)
+ expect(json_response.first['stats']['deletions']).to eq(commit.stats.deletions)
+ expect(json_response.first['stats']['total']).to eq(commit.stats.total)
+ end
+ end
+ end
+
context 'with pagination params' do
let(:page) { 1 }
let(:per_page) { 5 }
@@ -247,6 +264,19 @@ describe API::Commits do
]
}
end
+ let!(:valid_utf8_c_params) do
+ {
+ branch: 'master',
+ commit_message: message,
+ actions: [
+ {
+ action: 'create',
+ file_path: 'foo/bar/baz.txt',
+ content: 'puts 🦊'
+ }
+ ]
+ }
+ end
it 'a new file in project repo' do
post api(url, user), valid_c_params
@@ -257,6 +287,15 @@ describe API::Commits do
expect(json_response['committer_email']).to eq(user.email)
end
+ it 'a new file with utf8 chars in project repo' do
+ post api(url, user), valid_utf8_c_params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['title']).to eq(message)
+ expect(json_response['committer_name']).to eq(user.name)
+ expect(json_response['committer_email']).to eq(user.email)
+ end
+
it 'returns a 400 bad request if file exists' do
post api(url, user), invalid_c_params
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index bc32372d3a9..a56b913198c 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -522,7 +522,6 @@ describe API::Internal do
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 }
before do
diff --git a/spec/requests/api/project_import_spec.rb b/spec/requests/api/project_import_spec.rb
index f8c64f063af..97dffdc9233 100644
--- a/spec/requests/api/project_import_spec.rb
+++ b/spec/requests/api/project_import_spec.rb
@@ -3,7 +3,7 @@ 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(:file) { File.join('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)
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 9b7c3205c1f..99103039f77 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -518,7 +518,7 @@ describe API::Projects do
end
it 'uploads avatar for project a project' do
- project = attributes_for(:project, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif'))
+ project = attributes_for(:project, avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif'))
post api('/projects', user), project
@@ -777,7 +777,7 @@ describe API::Projects do
end
it "uploads the file and returns its info" do
- post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
+ post api("/projects/#{project.id}/uploads", user), file: fixture_file_upload("spec/fixtures/dk.png", "image/png")
expect(response).to have_gitlab_http_status(201)
expect(json_response['alt']).to eq("dk")
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index c981a10ac38..16e6f19773f 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -816,6 +816,18 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(job.reload.trace.raw).to eq 'BUILD TRACE'
end
+
+ context 'when running state is sent' do
+ it 'updates update_at value' do
+ expect { update_job_after_time }.to change { job.reload.updated_at }
+ end
+ end
+
+ context 'when other state is sent' do
+ it "doesn't update update_at value" do
+ expect { update_job_after_time(20.minutes, state: 'success') }.not_to change { job.reload.updated_at }
+ end
+ end
end
context 'when job has been erased' do
@@ -838,6 +850,7 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
expect(response).to have_gitlab_http_status(403)
+ expect(response.header['Job-Status']).to eq 'failed'
expect(job.trace.raw).to eq 'Job failed'
expect(job).to be_failed
end
@@ -847,6 +860,12 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
new_params = params.merge(token: token)
put api("/jobs/#{job.id}"), new_params
end
+
+ def update_job_after_time(update_interval = 20.minutes, state = 'running')
+ Timecop.travel(job.updated_at + update_interval) do
+ update_job(job.token, state: state)
+ end
+ end
end
describe 'PATCH /api/v4/jobs/:id/trace' do
@@ -979,6 +998,17 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
end
+
+ context 'when the job is canceled' do
+ before do
+ job.cancel
+ patch_the_trace
+ end
+
+ it 'receives status in header' do
+ expect(response.header['Job-Status']).to eq 'canceled'
+ end
+ end
end
context 'when Runner makes a force-patch' do
@@ -1055,8 +1085,8 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
let(:jwt_token) { JWT.encode({ 'iss' => 'gitlab-workhorse' }, Gitlab::Workhorse.secret, 'HS256') }
let(:headers) { { 'GitLab-Workhorse' => '1.0', Gitlab::Workhorse::INTERNAL_API_REQUEST_HEADER => jwt_token } }
let(:headers_with_token) { headers.merge(API::Helpers::Runner::JOB_TOKEN_HEADER => job.token) }
- let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
- let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') }
+ let(:file_upload) { fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
+ let(:file_upload2) { fixture_file_upload('spec/fixtures/dk.png', 'image/gif') }
before do
stub_artifacts_object_storage
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index b3e253befc6..c5456977b60 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -20,6 +20,7 @@ describe API::Snippets do
private_snippet.id)
expect(json_response.last).to have_key('web_url')
expect(json_response.last).to have_key('raw_url')
+ expect(json_response.last).to have_key('visibility')
end
it 'hides private snippets from regular user' do
@@ -112,6 +113,7 @@ describe API::Snippets do
expect(json_response['title']).to eq(snippet.title)
expect(json_response['description']).to eq(snippet.description)
expect(json_response['file_name']).to eq(snippet.file_name)
+ expect(json_response['visibility']).to eq(snippet.visibility)
end
it 'returns 404 for invalid snippet id' do
@@ -142,6 +144,7 @@ describe API::Snippets do
expect(json_response['title']).to eq(params[:title])
expect(json_response['description']).to eq(params[:description])
expect(json_response['file_name']).to eq(params[:file_name])
+ expect(json_response['visibility']).to eq(params[:visibility])
end
it 'returns 400 for missing parameters' do
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 05637eb0729..a97c3f3461a 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -512,7 +512,7 @@ describe API::Users do
end
it 'updates user with avatar' do
- put api("/users/#{user.id}", admin), { avatar: fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') }
+ put api("/users/#{user.id}", admin), { avatar: fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif') }
user.reload
@@ -1123,58 +1123,63 @@ describe API::Users do
describe "GET /user" do
let(:personal_access_token) { create(:personal_access_token, user: user).token }
- context 'with regular user' do
- context 'with personal access token' do
- it 'returns 403 without private token when sudo is defined' do
- get api("/user?private_token=#{personal_access_token}&sudo=123")
+ shared_examples 'get user info' do |version|
+ context 'with regular user' do
+ context 'with personal access token' do
+ it 'returns 403 without private token when sudo is defined' do
+ get api("/user?private_token=#{personal_access_token}&sudo=123", version: version)
- expect(response).to have_gitlab_http_status(403)
+ expect(response).to have_gitlab_http_status(403)
+ end
end
- end
- it 'returns current user without private token when sudo not defined' do
- get api("/user", user)
+ it 'returns current user without private token when sudo not defined' do
+ get api("/user", user, version: version)
- expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/user/public')
- expect(json_response['id']).to eq(user.id)
- end
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/user/public')
+ expect(json_response['id']).to eq(user.id)
+ end
- context "scopes" do
- let(:path) { "/user" }
- let(:api_call) { method(:api) }
+ context "scopes" do
+ let(:path) { "/user" }
+ let(:api_call) { method(:api) }
- include_examples 'allows the "read_user" scope'
+ include_examples 'allows the "read_user" scope', version
+ end
end
- end
- context 'with admin' do
- let(:admin_personal_access_token) { create(:personal_access_token, user: admin).token }
+ context 'with admin' do
+ let(:admin_personal_access_token) { create(:personal_access_token, user: admin).token }
- context 'with personal access token' do
- it 'returns 403 without private token when sudo defined' do
- get api("/user?private_token=#{admin_personal_access_token}&sudo=#{user.id}")
+ context 'with personal access token' do
+ it 'returns 403 without private token when sudo defined' do
+ get api("/user?private_token=#{admin_personal_access_token}&sudo=#{user.id}", version: version)
- expect(response).to have_gitlab_http_status(403)
- end
+ expect(response).to have_gitlab_http_status(403)
+ end
- it 'returns initial current user without private token but with is_admin when sudo not defined' do
- get api("/user?private_token=#{admin_personal_access_token}")
+ it 'returns initial current user without private token but with is_admin when sudo not defined' do
+ get api("/user?private_token=#{admin_personal_access_token}", version: version)
- expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/user/admin')
- expect(json_response['id']).to eq(admin.id)
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/user/admin')
+ expect(json_response['id']).to eq(admin.id)
+ end
end
end
- end
- context 'with unauthenticated user' do
- it "returns 401 error if user is unauthenticated" do
- get api("/user")
+ context 'with unauthenticated user' do
+ it "returns 401 error if user is unauthenticated" do
+ get api("/user", version: version)
- expect(response).to have_gitlab_http_status(401)
+ expect(response).to have_gitlab_http_status(401)
+ end
end
end
+
+ it_behaves_like 'get user info', 'v3'
+ it_behaves_like 'get user info', 'v4'
end
describe "GET /user/keys" do
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 2514dab1714..92fcfb65269 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -1,6 +1,7 @@
require "spec_helper"
describe 'Git HTTP requests' do
+ include ProjectForksHelper
include TermsHelper
include GitHttpHelpers
include WorkhorseHelpers
@@ -305,6 +306,22 @@ describe 'Git HTTP requests' do
expect(response.body).to eq(change_access_error(:push_code))
end
end
+
+ context 'when merge requests are open that allow maintainer access' do
+ let(:canonical_project) { create(:project, :public, :repository) }
+ let(:project) { fork_project(canonical_project, nil, repository: true) }
+
+ before do
+ canonical_project.add_master(user)
+ create(:merge_request,
+ source_project: project,
+ target_project: canonical_project,
+ source_branch: 'fixes',
+ allow_collaboration: true)
+ end
+
+ it_behaves_like 'pushes are allowed'
+ end
end
end
diff --git a/spec/requests/openid_connect_spec.rb b/spec/requests/openid_connect_spec.rb
index be286c490fe..bcb8d6c2bfc 100644
--- a/spec/requests/openid_connect_spec.rb
+++ b/spec/requests/openid_connect_spec.rb
@@ -61,7 +61,7 @@ describe 'OpenID Connect requests' do
email: private_email.email,
public_email: public_email.email,
website_url: 'https://example.com',
- avatar: fixture_file_upload(Rails.root + "spec/fixtures/dk.png")
+ avatar: fixture_file_upload('spec/fixtures/dk.png')
)
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index e1b4e618092..56d93095a85 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -36,33 +36,36 @@ describe 'project routing' do
shared_examples 'RESTful project resources' do
let(:actions) { [:index, :create, :new, :edit, :show, :update, :destroy] }
let(:controller_path) { controller }
+ let(:id) { { id: '1' } }
+ let(:format) { {} } # response format, e.g. { format: :html }
+ let(:params) { { namespace_id: 'gitlab', project_id: 'gitlabhq' } }
it 'to #index' do
- expect(get("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#index", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:index)
+ expect(get("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#index", params) if actions.include?(:index)
end
it 'to #create' do
- expect(post("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#create", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:create)
+ expect(post("/gitlab/gitlabhq/#{controller_path}")).to route_to("projects/#{controller}#create", params) if actions.include?(:create)
end
it 'to #new' do
- expect(get("/gitlab/gitlabhq/#{controller_path}/new")).to route_to("projects/#{controller}#new", namespace_id: 'gitlab', project_id: 'gitlabhq') if actions.include?(:new)
+ expect(get("/gitlab/gitlabhq/#{controller_path}/new")).to route_to("projects/#{controller}#new", params) if actions.include?(:new)
end
it 'to #edit' do
- expect(get("/gitlab/gitlabhq/#{controller_path}/1/edit")).to route_to("projects/#{controller}#edit", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:edit)
+ expect(get("/gitlab/gitlabhq/#{controller_path}/1/edit")).to route_to("projects/#{controller}#edit", params.merge(**id, **format)) if actions.include?(:edit)
end
it 'to #show' do
- expect(get("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#show", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:show)
+ expect(get("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#show", params.merge(**id, **format)) if actions.include?(:show)
end
it 'to #update' do
- expect(put("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#update", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:update)
+ expect(put("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#update", params.merge(id)) if actions.include?(:update)
end
it 'to #destroy' do
- expect(delete("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#destroy", namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1') if actions.include?(:destroy)
+ expect(delete("/gitlab/gitlabhq/#{controller_path}/1")).to route_to("projects/#{controller}#destroy", params.merge(**id, **format)) if actions.include?(:destroy)
end
end
@@ -150,12 +153,13 @@ describe 'project routing' do
end
it 'to #history' do
- expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
+ expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1', format: :html)
end
it_behaves_like 'RESTful project resources' do
let(:actions) { [:create, :edit, :show, :destroy] }
let(:controller) { 'wikis' }
+ let(:format) { { format: :html } }
end
end
diff --git a/spec/serializers/build_serializer_spec.rb b/spec/serializers/build_serializer_spec.rb
index 98cd15e248b..52459cd369d 100644
--- a/spec/serializers/build_serializer_spec.rb
+++ b/spec/serializers/build_serializer_spec.rb
@@ -39,7 +39,7 @@ describe BuildSerializer do
expect(subject[:label]).to eq('failed')
expect(subject[:tooltip]).to eq('failed <br> (unknown failure)')
expect(subject[:icon]).to eq(status.icon)
- expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
+ expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
end
end
@@ -54,7 +54,7 @@ describe BuildSerializer do
expect(subject[:label]).to eq('passed')
expect(subject[:tooltip]).to eq('passed')
expect(subject[:icon]).to eq(status.icon)
- expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
+ expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
end
end
end
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index e0e6eecb300..eb4235e3ee6 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -179,7 +179,7 @@ describe PipelineSerializer do
expect(subject[:text]).to eq(status.text)
expect(subject[:label]).to eq(status.label)
expect(subject[:icon]).to eq(status.icon)
- expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.ico")
+ expect(subject[:favicon]).to match_asset_path("/assets/ci_favicons/#{status.favicon}.png")
end
end
end
diff --git a/spec/serializers/status_entity_spec.rb b/spec/serializers/status_entity_spec.rb
index 559475e571c..0b010ebd507 100644
--- a/spec/serializers/status_entity_spec.rb
+++ b/spec/serializers/status_entity_spec.rb
@@ -18,17 +18,7 @@ describe StatusEntity do
it 'contains status details' do
expect(subject).to include :text, :icon, :favicon, :label, :group, :tooltip
expect(subject).to include :has_details, :details_path
- expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.ico')
- end
-
- it 'contains a dev namespaced favicon if dev env' do
- allow(Rails.env).to receive(:development?) { true }
- expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/dev/favicon_status_success.ico')
- end
-
- it 'contains a canary namespaced favicon if canary env' do
- stub_env('CANARY', 'true')
- expect(entity.as_json[:favicon]).to match_asset_path('/assets/ci_favicons/canary/favicon_status_success.ico')
+ expect(subject[:favicon]).to match_asset_path('/assets/ci_favicons/favicon_status_success.png')
end
end
end
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
index 33405d7a7ec..92159e1e372 100644
--- a/spec/services/git_tag_push_service_spec.rb
+++ b/spec/services/git_tag_push_service_spec.rb
@@ -118,7 +118,9 @@ describe GitTagPushService do
before do
# Create the lightweight tag
- project.repository.raw_repository.rugged.tags.create(tag_name, newrev)
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.raw_repository.rugged.tags.create(tag_name, newrev)
+ end
# Clear tag list cache
project.repository.expire_tags_cache
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 38d84cf0ceb..b1882df732d 100644
--- a/spec/services/merge_requests/create_from_issue_service_spec.rb
+++ b/spec/services/merge_requests/create_from_issue_service_spec.rb
@@ -125,9 +125,14 @@ describe MergeRequests::CreateFromIssueService do
end
context 'when ref branch does not exist' do
- it 'does not create a merge request' do
- expect { described_class.new(project, user, issue_iid: issue.iid, ref: 'nobr').execute }
- .not_to change { project.merge_requests.count }
+ subject { described_class.new(project, user, issue_iid: issue.iid, ref: 'no-such-branch').execute }
+
+ it 'creates a merge request' do
+ expect { subject }.to change(project.merge_requests, :count).by(1)
+ end
+
+ it 'sets the merge request target branch to the project default branch' do
+ expect(subject[:merge_request].target_branch).to eq(project.default_branch)
end
end
end
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index 5ef6365fcc9..28f56d19657 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -72,7 +72,7 @@ describe MergeRequests::FfMergeService do
it 'logs and saves error if there is an PreReceiveError exception' do
error_message = 'error message'
- allow(service).to receive(:repository).and_raise(Gitlab::Git::HooksService::PreReceiveError, error_message)
+ allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message)
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index dc30a9bccc1..ef2738ef504 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -226,7 +226,7 @@ describe MergeRequests::MergeService do
it 'logs and saves error if there is an PreReceiveError exception' do
error_message = 'error message'
- allow(service).to receive(:repository).and_raise(Gitlab::Git::HooksService::PreReceiveError, error_message)
+ allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message)
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
diff --git a/spec/services/merge_requests/squash_service_spec.rb b/spec/services/merge_requests/squash_service_spec.rb
index bd884787425..ded17fa92a4 100644
--- a/spec/services/merge_requests/squash_service_spec.rb
+++ b/spec/services/merge_requests/squash_service_spec.rb
@@ -63,7 +63,9 @@ describe MergeRequests::SquashService do
end
it 'has the same diff as the merge request, but a different SHA' do
- rugged = project.repository.rugged
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
mr_diff = rugged.diff(merge_request.diff_base_sha, merge_request.diff_head_sha)
squash_diff = rugged.diff(merge_request.diff_start_sha, squash_sha)
diff --git a/spec/services/notification_recipient_service_spec.rb b/spec/services/notification_recipient_service_spec.rb
new file mode 100644
index 00000000000..7f536ce4e68
--- /dev/null
+++ b/spec/services/notification_recipient_service_spec.rb
@@ -0,0 +1,36 @@
+require 'spec_helper'
+
+describe NotificationRecipientService do
+ let(:service) { described_class }
+ let(:assignee) { create(:user) }
+ let(:project) { create(:project, :public) }
+ let(:other_projects) { create_list(:project, 5, :public) }
+
+ describe '#build_new_note_recipients' do
+ let(:issue) { create(:issue, project: project, assignees: [assignee]) }
+ let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) }
+
+ def create_watcher
+ watcher = create(:user)
+ create(:notification_setting, project: project, user: watcher, level: :watch)
+
+ other_projects.each do |other_project|
+ create(:notification_setting, project: other_project, user: watcher, level: :watch)
+ end
+ end
+
+ it 'avoids N+1 queries', :request_store do
+ Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher }
+
+ service.build_new_note_recipients(note)
+
+ control_count = ActiveRecord::QueryRecorder.new do
+ service.build_new_note_recipients(note)
+ end
+
+ Gitlab::GitalyClient.allow_n_plus_1_calls { create_watcher }
+
+ expect { service.build_new_note_recipients(note) }.not_to exceed_query_limit(control_count)
+ end
+ end
+end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 831678b949d..0eadc83bfe3 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -59,6 +59,20 @@ describe NotificationService, :mailer do
should_email(participant)
end
+
+ context 'for subgroups', :nested_groups do
+ before do
+ build_group(project)
+ end
+
+ it 'emails the participant' do
+ create(:note_on_issue, noteable: issuable, project_id: project.id, note: 'anything', author: @pg_participant)
+
+ notification_trigger
+
+ should_email_nested_group_user(@pg_participant)
+ end
+ end
end
shared_examples 'participating by assignee notification' do
@@ -239,34 +253,56 @@ describe NotificationService, :mailer do
end
describe 'new note on issue in project that belongs to a group' do
- let(:group) { create(:group) }
-
before do
note.project.namespace_id = group.id
- note.project.group.add_user(@u_watcher, GroupMember::MASTER)
- note.project.group.add_user(@u_custom_global, GroupMember::MASTER)
+ group.add_user(@u_watcher, GroupMember::MASTER)
+ group.add_user(@u_custom_global, GroupMember::MASTER)
note.project.save
@u_watcher.notification_settings_for(note.project).participating!
- @u_watcher.notification_settings_for(note.project.group).global!
+ @u_watcher.notification_settings_for(group).global!
update_custom_notification(:new_note, @u_custom_global)
reset_delivered_emails!
end
- it do
- notification.new_note(note)
+ shared_examples 'new note notifications' do
+ it do
+ notification.new_note(note)
+
+ should_email(note.noteable.author)
+ should_email(note.noteable.assignees.first)
+ should_email(@u_mentioned)
+ should_email(@u_custom_global)
+ should_not_email(@u_guest_custom)
+ should_not_email(@u_guest_watcher)
+ should_not_email(@u_watcher)
+ should_not_email(note.author)
+ should_not_email(@u_participating)
+ should_not_email(@u_disabled)
+ should_not_email(@u_lazy_participant)
+ end
+ end
- should_email(note.noteable.author)
- should_email(note.noteable.assignees.first)
- should_email(@u_mentioned)
- should_email(@u_custom_global)
- should_not_email(@u_guest_custom)
- should_not_email(@u_guest_watcher)
- should_not_email(@u_watcher)
- should_not_email(note.author)
- should_not_email(@u_participating)
- should_not_email(@u_disabled)
- should_not_email(@u_lazy_participant)
+ let(:group) { create(:group) }
+
+ it_behaves_like 'new note notifications'
+
+ context 'which is a subgroup', :nested_groups do
+ let!(:parent) { create(:group) }
+ let!(:group) { create(:group, parent: parent) }
+
+ it_behaves_like 'new note notifications'
+
+ it 'overrides child objects with global level' do
+ user = create(:user)
+ parent.add_developer(user)
+ user.notification_settings_for(parent).watch!
+ reset_delivered_emails!
+
+ notification.new_note(note)
+
+ should_email(user)
+ end
end
end
end
@@ -301,6 +337,31 @@ describe NotificationService, :mailer do
should_email(member)
should_email(admin)
end
+
+ context 'on project that belongs to subgroup', :nested_groups do
+ let(:group_reporter) { create(:user) }
+ let(:group_guest) { create(:user) }
+ let(:parent_group) { create(:group) }
+ let(:child_group) { create(:group, parent: parent_group) }
+ let(:project) { create(:project, namespace: child_group) }
+
+ context 'when user is group guest member' do
+ before do
+ parent_group.add_reporter(group_reporter)
+ parent_group.add_guest(group_guest)
+ group_guest.notification_settings_for(parent_group).watch!
+ group_reporter.notification_settings_for(parent_group).watch!
+ reset_delivered_emails!
+ end
+
+ it 'does not email guest user' do
+ notification.new_note(note)
+
+ should_email(group_reporter)
+ should_not_email(group_guest)
+ end
+ end
+ end
end
context 'issue note mention' do
@@ -311,6 +372,7 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
+ build_group(note.project)
note.project.add_master(note.author)
add_users_with_subscription(note.project, issue)
reset_delivered_emails!
@@ -336,10 +398,20 @@ describe NotificationService, :mailer do
should_email(@u_guest_watcher)
should_email(note.noteable.author)
should_email(note.noteable.assignees.first)
- should_not_email(note.author)
+ should_email_nested_group_user(@pg_watcher)
should_email(@u_mentioned)
- should_not_email(@u_disabled)
should_email(@u_not_mentioned)
+ should_not_email(note.author)
+ should_not_email(@u_disabled)
+ should_not_email_nested_group_user(@pg_disabled)
+ end
+
+ it 'notifies parent group members with mention level', :nested_groups do
+ note = create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: "@#{@pg_mention.username}")
+
+ notification.new_note(note)
+
+ should_email_nested_group_user(@pg_mention)
end
it 'filters out "mentioned in" notes' do
@@ -352,17 +424,18 @@ describe NotificationService, :mailer do
end
context 'project snippet note' do
- let(:project) { create(:project, :public) }
+ let!(:project) { create(:project, :public) }
let(:snippet) { create(:project_snippet, project: project, author: create(:user)) }
- let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') }
+ let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: project.id, note: '@all mentioned') }
before do
- build_team(note.project)
+ build_team(project)
+ build_group(project)
# make sure these users can read the project snippet!
project.add_guest(@u_guest_watcher)
project.add_guest(@u_guest_custom)
-
+ add_member_for_parent_group(@pg_watcher, project)
note.project.add_master(note.author)
reset_delivered_emails!
end
@@ -370,7 +443,6 @@ describe NotificationService, :mailer do
describe '#new_note' do
it 'notifies the team members' do
notification.new_note(note)
-
# Notify all team members
note.project.team.members.each do |member|
# User with disabled notification should not be notified
@@ -449,6 +521,7 @@ describe NotificationService, :mailer do
before do
build_team(note.project)
+ build_group(project)
reset_delivered_emails!
allow(note.noteable).to receive(:author).and_return(@u_committer)
update_custom_notification(:new_note, @u_guest_custom, resource: project)
@@ -463,11 +536,13 @@ describe NotificationService, :mailer do
should_email(@u_guest_custom)
should_email(@u_committer)
should_email(@u_watcher)
+ should_email_nested_group_user(@pg_watcher)
should_not_email(@u_mentioned)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
+ should_not_email_nested_group_user(@pg_disabled)
end
it do
@@ -478,10 +553,12 @@ describe NotificationService, :mailer do
should_email(@u_committer)
should_email(@u_watcher)
should_email(@u_mentioned)
+ should_email_nested_group_user(@pg_watcher)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
+ should_not_email_nested_group_user(@pg_disabled)
end
it do
@@ -548,10 +625,13 @@ describe NotificationService, :mailer do
should_email(@g_global_watcher)
should_email(@g_watcher)
should_email(@unsubscribed_mentioned)
+ should_email_nested_group_user(@pg_watcher)
should_not_email(@u_mentioned)
should_not_email(@u_participating)
should_not_email(@u_disabled)
should_not_email(@u_lazy_participant)
+ should_not_email_nested_group_user(@pg_disabled)
+ should_not_email_nested_group_user(@pg_mention)
end
it do
@@ -1922,19 +2002,69 @@ describe NotificationService, :mailer do
# Users in the project's group but not part of project's team
# with different notification settings
def build_group(project)
- group = create(:group, :public)
- project.group = group
+ group = create_nested_group
+ project.update(namespace_id: group.id)
# Group member: global=disabled, group=watch
- @g_watcher = create_user_with_notification(:watch, 'group_watcher', project.group)
+ @g_watcher ||= create_user_with_notification(:watch, 'group_watcher', project.group)
@g_watcher.notification_settings_for(nil).disabled!
# Group member: global=watch, group=global
- @g_global_watcher = create_global_setting_for(create(:user), :watch)
+ @g_global_watcher ||= create_global_setting_for(create(:user), :watch)
group.add_users([@g_watcher, @g_global_watcher], :master)
+
group
end
+ # Creates a nested group only if supported
+ # to avoid errors on MySQL
+ def create_nested_group
+ if Group.supports_nested_groups?
+ parent_group = create(:group, :public)
+ child_group = create(:group, :public, parent: parent_group)
+
+ # Parent group member: global=disabled, parent_group=watch, child_group=global
+ @pg_watcher ||= create_user_with_notification(:watch, 'parent_group_watcher', parent_group)
+ @pg_watcher.notification_settings_for(nil).disabled!
+
+ # Parent group member: global=global, parent_group=disabled, child_group=global
+ @pg_disabled ||= create_user_with_notification(:disabled, 'parent_group_disabled', parent_group)
+ @pg_disabled.notification_settings_for(nil).global!
+
+ # Parent group member: global=global, parent_group=mention, child_group=global
+ @pg_mention ||= create_user_with_notification(:mention, 'parent_group_mention', parent_group)
+ @pg_mention.notification_settings_for(nil).global!
+
+ # Parent group member: global=global, parent_group=participating, child_group=global
+ @pg_participant ||= create_user_with_notification(:participating, 'parent_group_participant', parent_group)
+ @pg_mention.notification_settings_for(nil).global!
+
+ child_group
+ else
+ create(:group, :public)
+ end
+ end
+
+ def add_member_for_parent_group(user, project)
+ return unless Group.supports_nested_groups?
+
+ project.reload
+
+ project.group.parent.add_master(user)
+ end
+
+ def should_email_nested_group_user(user, times: 1, recipients: email_recipients)
+ return unless Group.supports_nested_groups?
+
+ should_email(user, times: 1, recipients: email_recipients)
+ end
+
+ def should_not_email_nested_group_user(user, recipients: email_recipients)
+ return unless Group.supports_nested_groups?
+
+ should_not_email(user, recipients: email_recipients)
+ end
+
def add_users_with_subscription(project, issuable)
@subscriber = create :user
@unsubscriber = create :user
diff --git a/spec/services/projects/after_import_service_spec.rb b/spec/services/projects/after_import_service_spec.rb
index c6678fc1f5c..cd52bc88f4c 100644
--- a/spec/services/projects/after_import_service_spec.rb
+++ b/spec/services/projects/after_import_service_spec.rb
@@ -32,7 +32,7 @@ describe Projects::AfterImportService do
end
it 'removes refs/pull/**/*' do
- expect(repository.rugged.references.map(&:name))
+ expect(rugged.references.map(&:name))
.not_to include(%r{\Arefs/pull/})
end
end
@@ -46,10 +46,14 @@ describe Projects::AfterImportService do
end
it "does not remove refs/#{name}/tmp" do
- expect(repository.rugged.references.map(&:name))
+ expect(rugged.references.map(&:name))
.to include("refs/#{name}/tmp")
end
end
end
+
+ def rugged
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access { repository.rugged }
+ end
end
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index a8f003b1073..e8cbf84e3be 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -272,8 +272,11 @@ describe Projects::CreateService, '#execute' do
it 'writes project full path to .git/config' do
project = create_project(user, opts)
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ expect(rugged.config['gitlab.fullpath']).to eq project.full_path
end
def create_project(user, opts)
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index b63f409579e..38660ad7a01 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -5,7 +5,11 @@ describe Projects::DestroyService do
let!(:user) { create(:user) }
let!(:project) { create(:project, :repository, namespace: user.namespace) }
- let!(:path) { project.repository.path_to_repo }
+ let!(:path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path_to_repo
+ end
+ end
let!(:remove_path) { path.sub(/\.git\Z/, "+#{project.id}+deleted.git") }
let!(:async) { false } # execute or async_execute
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index a93f6f1ddc2..c15f5120b8a 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -8,7 +8,7 @@ describe Projects::ForkService do
before do
@from_user = create(:user)
@from_namespace = @from_user.namespace
- avatar = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png")
+ avatar = fixture_file_upload("spec/fixtures/dk.png", "image/png")
@from_project = create(:project,
:repository,
creator_id: @from_user.id,
diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb
index ee1a886f5d6..0a898e9b89b 100644
--- a/spec/services/projects/gitlab_projects_import_service_spec.rb
+++ b/spec/services/projects/gitlab_projects_import_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::GitlabProjectsImportService do
set(:namespace) { create(:namespace) }
let(:path) { 'test-path' }
- let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:file) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
let(:overwrite) { false }
let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } }
subject { described_class.new(namespace.owner, import_params) }
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 7dca81eb59e..ed4930313c5 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -37,7 +37,11 @@ describe Projects::HashedStorage::MigrateRepositoryService do
it 'writes project full path to .git/config' do
service.execute
- expect(project.repository.rugged.config['gitlab.fullpath']).to eq project.full_path
+ rugged_config = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged.config['gitlab.fullpath']
+ end
+
+ expect(rugged_config).to eq project.full_path
end
end
diff --git a/spec/services/projects/housekeeping_service_spec.rb b/spec/services/projects/housekeeping_service_spec.rb
index b7b5de07380..1cf373d1d72 100644
--- a/spec/services/projects/housekeeping_service_spec.rb
+++ b/spec/services/projects/housekeeping_service_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe Projects::HousekeepingService do
subject { described_class.new(project) }
- let(:project) { create(:project, :repository) }
+ set(:project) { create(:project, :repository) }
before do
project.reset_pushes_since_gc
@@ -16,12 +16,12 @@ describe Projects::HousekeepingService do
it 'enqueues a sidekiq job' do
expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid)
expect(subject).to receive(:lease_key).and_return(:the_lease_key)
- expect(subject).to receive(:task).and_return(:the_task)
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :the_task, :the_lease_key, :the_uuid)
+ expect(subject).to receive(:task).and_return(:incremental_repack)
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid).and_call_original
- subject.execute
-
- expect(project.reload.pushes_since_gc).to eq(0)
+ Sidekiq::Testing.fake! do
+ expect { subject.execute }.to change(GitGarbageCollectWorker.jobs, :size).by(1)
+ end
end
it 'yields the block if given' do
@@ -30,6 +30,16 @@ describe Projects::HousekeepingService do
end.to yield_with_no_args
end
+ it 'resets counter after execution' do
+ expect(subject).to receive(:try_obtain_lease).and_return(:the_uuid)
+ allow(subject).to receive(:gc_period).and_return(1)
+ project.increment_pushes_since_gc
+
+ Sidekiq::Testing.inline! do
+ expect { subject.execute }.to change { project.pushes_since_gc }.to(0)
+ end
+ end
+
context 'when no lease can be obtained' do
before do
expect(subject).to receive(:try_obtain_lease).and_return(false)
@@ -54,6 +64,30 @@ describe Projects::HousekeepingService do
end.not_to yield_with_no_args
end
end
+
+ context 'task type' do
+ it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do
+ allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid)
+ allow(subject).to receive(:lease_key).and_return(:the_lease_key)
+
+ # At push 200
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid)
+ .exactly(1).times
+ # At push 50, 100, 150
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid)
+ .exactly(3).times
+ # At push 10, 20, ... (except those above)
+ expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid)
+ .exactly(16).times
+
+ 201.times do
+ subject.increment!
+ subject.execute if subject.needed?
+ end
+
+ expect(project.pushes_since_gc).to eq(1)
+ end
+ end
end
describe '#needed?' do
@@ -69,31 +103,7 @@ describe Projects::HousekeepingService do
describe '#increment!' do
it 'increments the pushes_since_gc counter' do
- expect do
- subject.increment!
- end.to change { project.pushes_since_gc }.from(0).to(1)
+ expect { subject.increment! }.to change { project.pushes_since_gc }.by(1)
end
end
-
- it 'goes through all three housekeeping tasks, executing only the highest task when there is overlap' do
- allow(subject).to receive(:try_obtain_lease).and_return(:the_uuid)
- allow(subject).to receive(:lease_key).and_return(:the_lease_key)
-
- # At push 200
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :gc, :the_lease_key, :the_uuid)
- .exactly(1).times
- # At push 50, 100, 150
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :full_repack, :the_lease_key, :the_uuid)
- .exactly(3).times
- # At push 10, 20, ... (except those above)
- expect(GitGarbageCollectWorker).to receive(:perform_async).with(project.id, :incremental_repack, :the_lease_key, :the_uuid)
- .exactly(16).times
-
- 201.times do
- subject.increment!
- subject.execute if subject.needed?
- end
-
- expect(project.pushes_since_gc).to eq(1)
- end
end
diff --git a/spec/services/projects/participants_service_spec.rb b/spec/services/projects/participants_service_spec.rb
index 0d18ceb8ff8..6040f9100f8 100644
--- a/spec/services/projects/participants_service_spec.rb
+++ b/spec/services/projects/participants_service_spec.rb
@@ -4,7 +4,7 @@ describe Projects::ParticipantsService do
describe '#groups' do
describe 'avatar_url' do
let(:project) { create(:project, :public) }
- let(:group) { create(:group, avatar: fixture_file_upload(Rails.root + 'spec/fixtures/dk.png')) }
+ let(:group) { create(:group, avatar: fixture_file_upload('spec/fixtures/dk.png')) }
let(:user) { create(:user) }
let!(:group_member) { create(:group_member, group: group, user: user) }
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index 347ac13828c..1bffeee6790 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -4,13 +4,13 @@ describe Projects::UpdatePagesService do
set(:project) { create(:project, :repository) }
set(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit('HEAD').sha) }
set(:build) { create(:ci_build, pipeline: pipeline, ref: 'HEAD') }
- let(:invalid_file) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png') }
+ let(:invalid_file) { fixture_file_upload('spec/fixtures/dk.png') }
let(:extension) { 'zip' }
- let(:file) { fixture_file_upload(Rails.root + "spec/fixtures/pages.#{extension}") }
- let(:empty_file) { fixture_file_upload(Rails.root + "spec/fixtures/pages_empty.#{extension}") }
+ let(:file) { fixture_file_upload("spec/fixtures/pages.#{extension}") }
+ let(:empty_file) { fixture_file_upload("spec/fixtures/pages_empty.#{extension}") }
let(:metadata) do
- filename = Rails.root + "spec/fixtures/pages.#{extension}.meta"
+ filename = "spec/fixtures/pages.#{extension}.meta"
fixture_file_upload(filename) if File.exist?(filename)
end
@@ -196,8 +196,8 @@ describe Projects::UpdatePagesService do
let(:metadata) { spy('metadata') }
before do
- file = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip')
- metafile = fixture_file_upload(Rails.root + 'spec/fixtures/pages.zip.meta')
+ file = fixture_file_upload('spec/fixtures/pages.zip')
+ metafile = fixture_file_upload('spec/fixtures/pages.zip.meta')
build.update_attributes(legacy_artifacts_file: file)
build.update_attributes(legacy_artifacts_metadata: metafile)
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index bd835a1fca6..743e281183e 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -323,6 +323,14 @@ describe QuickActions::InterpretService do
end
end
+ shared_examples 'confidential command' do
+ it 'marks issue as confidential if content contains /confidential' do
+ _, updates = service.execute(content, issuable)
+
+ expect(updates).to eq(confidential: true)
+ end
+ end
+
shared_examples 'shrug command' do
it 'appends ¯\_(ツ)_/¯ to the comment' do
new_content, _ = service.execute(content, issuable)
@@ -774,6 +782,11 @@ describe QuickActions::InterpretService do
let(:issuable) { issue }
end
+ it_behaves_like 'confidential command' do
+ let(:content) { '/confidential' }
+ let(:issuable) { issue }
+ end
+
context '/copy_metadata command' do
let(:todo_label) { create(:label, project: project, title: 'To Do') }
let(:inreview_label) { create(:label, project: project, title: 'In Review') }
@@ -919,6 +932,11 @@ describe QuickActions::InterpretService do
end
it_behaves_like 'empty command' do
+ let(:content) { '/confidential' }
+ let(:issuable) { issue }
+ end
+
+ it_behaves_like 'empty command' do
let(:content) { '/duplicate #{issue.to_reference}' }
let(:issuable) { issue }
end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index e28b0ea5cf2..57d081cffb3 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe SystemNoteService do
include Gitlab::Routing
include RepoHelpers
+ include AssetsHelpers
set(:group) { create(:group) }
set(:project) { create(:project, :repository, group: group) }
@@ -769,6 +770,8 @@ describe SystemNoteService do
end
describe "new reference" do
+ let(:favicon_path) { "http://localhost/assets/#{find_asset('favicon.png').digest_path}" }
+
before do
allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
end
@@ -789,7 +792,7 @@ describe SystemNoteService do
object: {
url: project_commit_url(project, commit),
title: "GitLab: Mentioned on commit - #{commit.title}",
- icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" },
+ icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: false }
}
)
@@ -815,7 +818,7 @@ describe SystemNoteService do
object: {
url: project_issue_url(project, issue),
title: "GitLab: Mentioned on issue - #{issue.title}",
- icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" },
+ icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: false }
}
)
@@ -841,7 +844,7 @@ describe SystemNoteService do
object: {
url: project_snippet_url(project, snippet),
title: "GitLab: Mentioned on snippet - #{snippet.title}",
- icon: { title: "GitLab", url16x16: "http://localhost/favicon.ico" },
+ icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: false }
}
)
diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb
index e7e9080b6b0..0cbe57352be 100644
--- a/spec/services/tags/create_service_spec.rb
+++ b/spec/services/tags/create_service_spec.rb
@@ -41,7 +41,7 @@ describe Tags::CreateService do
it 'returns an error' do
expect(repository).to receive(:add_tag)
.with(user, 'v1.1.0', 'master', 'Foo')
- .and_raise(Gitlab::Git::HooksService::PreReceiveError, 'something went wrong')
+ .and_raise(Gitlab::Git::PreReceiveError, 'something went wrong')
response = service.execute('v1.1.0', 'master', 'Foo')
diff --git a/spec/services/upload_service_spec.rb b/spec/services/upload_service_spec.rb
index 24f3a5c5ff0..9b232a52efa 100644
--- a/spec/services/upload_service_spec.rb
+++ b/spec/services/upload_service_spec.rb
@@ -9,7 +9,7 @@ describe UploadService do
context 'for valid gif file' do
before do
- gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
+ gif = fixture_file_upload('spec/fixtures/banana_sample.gif', 'image/gif')
@link_to_file = upload_file(@project, gif)
end
@@ -21,7 +21,7 @@ describe UploadService do
context 'for valid png file' do
before do
- png = fixture_file_upload(Rails.root + 'spec/fixtures/dk.png',
+ png = fixture_file_upload('spec/fixtures/dk.png',
'image/png')
@link_to_file = upload_file(@project, png)
end
@@ -34,7 +34,7 @@ describe UploadService do
context 'for valid jpg file' do
before do
- jpg = fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg')
+ jpg = fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
@link_to_file = upload_file(@project, jpg)
end
@@ -46,7 +46,7 @@ describe UploadService do
context 'for txt file' do
before do
- txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain')
+ txt = fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain')
@link_to_file = upload_file(@project, txt)
end
@@ -58,7 +58,7 @@ describe UploadService do
context 'for too large a file' do
before do
- txt = fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain')
+ txt = fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain')
allow(txt).to receive(:size) { 1000.megabytes.to_i }
@link_to_file = upload_file(@project, txt)
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index e093444121a..8417b340de5 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -107,19 +107,6 @@ RSpec.configure do |config|
end
config.before(:example) do
- # Skip pre-receive hook check so we can use the web editor and merge.
- allow_any_instance_of(Gitlab::Git::Hook).to receive(:trigger).and_return([true, nil])
-
- allow_any_instance_of(Gitlab::Git::GitlabProjects).to receive(:fork_repository).and_wrap_original do |m, *args|
- m.call(*args)
-
- shard_name, repository_relative_path = args
- # We can't leave the hooks in place after a fork, as those would fail in tests
- # The "internal" API is not available
- Gitlab::Shell.new.rm_directory(shard_name,
- File.join(repository_relative_path, 'hooks'))
- end
-
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
end
diff --git a/spec/support/api/scopes/read_user_shared_examples.rb b/spec/support/api/scopes/read_user_shared_examples.rb
index 06ae8792c61..d7cef137989 100644
--- a/spec/support/api/scopes/read_user_shared_examples.rb
+++ b/spec/support/api/scopes/read_user_shared_examples.rb
@@ -1,10 +1,12 @@
-shared_examples_for 'allows the "read_user" scope' do
+shared_examples_for 'allows the "read_user" scope' do |api_version|
+ let(:version) { api_version || 'v4' }
+
context 'for personal access tokens' do
context 'when the requesting token has the "api" scope' do
let(:token) { create(:personal_access_token, scopes: ['api'], user: user) }
it 'returns a "200" response' do
- get api_call.call(path, user, personal_access_token: token)
+ get api_call.call(path, user, personal_access_token: token, version: version)
expect(response).to have_gitlab_http_status(200)
end
@@ -14,7 +16,7 @@ shared_examples_for 'allows the "read_user" scope' do
let(:token) { create(:personal_access_token, scopes: ['read_user'], user: user) }
it 'returns a "200" response' do
- get api_call.call(path, user, personal_access_token: token)
+ get api_call.call(path, user, personal_access_token: token, version: version)
expect(response).to have_gitlab_http_status(200)
end
@@ -28,7 +30,7 @@ shared_examples_for 'allows the "read_user" scope' do
end
it 'returns a "403" response' do
- get api_call.call(path, user, personal_access_token: token)
+ get api_call.call(path, user, personal_access_token: token, version: version)
expect(response).to have_gitlab_http_status(403)
end
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 836e5e7be23..b4c71d69119 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -1,6 +1,7 @@
require 'spec_helper'
shared_examples 'reportable note' do |type|
+ include MobileHelpers
include NotesHelper
let(:comment) { find("##{ActionView::RecordIdentifier.dom_id(note)}") }
@@ -39,6 +40,9 @@ shared_examples 'reportable note' do |type|
end
def open_dropdown(dropdown)
+ # make window wide enough that tooltip doesn't trigger horizontal scrollbar
+ resize_window(1200, 800)
+
dropdown.find('.more-actions-toggle').click
dropdown.find('.dropdown-menu li', match: :first)
end
diff --git a/spec/support/gitaly.rb b/spec/support/gitaly.rb
index 5a1dd44bc9d..614aaa73693 100644
--- a/spec/support/gitaly.rb
+++ b/spec/support/gitaly.rb
@@ -9,7 +9,7 @@ RSpec.configure do |config|
# Use 'and_wrap_original' to make sure the arguments are valid
allow(Gitlab::GitalyClient).to receive(:feature_enabled?).and_wrap_original do |m, *args|
m.call(*args)
- !Gitlab::GitalyClient::EXPLICIT_OPT_IN_REQUIRED.include?(args.first)
+ !Gitlab::GitalyClient.explicit_opt_in_required.include?(args.first)
end
end
end
diff --git a/spec/support/helpers/assets_helpers.rb b/spec/support/helpers/assets_helpers.rb
new file mode 100644
index 00000000000..09bbf451671
--- /dev/null
+++ b/spec/support/helpers/assets_helpers.rb
@@ -0,0 +1,15 @@
+module AssetsHelpers
+ # In a CI environment the assets are not compiled, as there is a CI job
+ # `compile-assets` that compiles them in the prepare stage for all following
+ # specs.
+ # Locally the assets are precompiled dynamically.
+ #
+ # Sprockets doesn't provide one method to access an asset for both cases.
+ def find_asset(asset_name)
+ if ENV['CI']
+ Sprockets::Railtie.build_environment(Rails.application, true)[asset_name]
+ else
+ Rails.application.assets.find_asset(asset_name)
+ end
+ end
+end
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 1fef50a52ec..05a8e6206ae 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -135,6 +135,16 @@ module TestEnv
install_dir: Gitlab.config.gitlab_shell.path,
version: Gitlab::Shell.version_required,
task: 'gitlab:shell:install')
+
+ create_fake_git_hooks
+ end
+
+ def create_fake_git_hooks
+ # gitlab-shell hooks don't work in our test environment because they try to make internal API calls
+ hooks_dir = File.join(Gitlab.config.gitlab_shell.path, 'hooks')
+ %w[pre-receive post-receive update].each do |hook|
+ File.open(File.join(hooks_dir, hook), 'w', 0755) { |f| f.puts '#!/bin/sh' }
+ end
end
def setup_gitaly
diff --git a/spec/support/rspec.rb b/spec/support/rspec.rb
index dffab22d8b5..54b8df7aa19 100644
--- a/spec/support/rspec.rb
+++ b/spec/support/rspec.rb
@@ -9,4 +9,6 @@ RSpec.configure do |config|
config.include StubConfiguration
config.include StubObjectStorage
config.include StubENV
+
+ config.fixture_path = Rails.root if defined?(Rails)
end
diff --git a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
index ea7dbade171..bbbad86dcd5 100644
--- a/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
+++ b/spec/support/shared_examples/controllers/uploads_actions_shared_examples.rb
@@ -1,7 +1,7 @@
shared_examples 'handle uploads' do
let(:user) { create(:user) }
- let(:jpg) { fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg') }
- let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
+ let(:jpg) { fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg') }
+ let(:txt) { fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain') }
let(:secret) { FileUploader.generate_secret }
let(:uploader_class) { FileUploader }
diff --git a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
index 6352f1527cd..19800c6638f 100644
--- a/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
+++ b/spec/support/shared_examples/uploaders/object_storage_shared_examples.rb
@@ -76,26 +76,24 @@ shared_examples "migrates" do |to_store:, from_store: nil|
end
context 'when migrate! is occupied by another process' do
- let(:exclusive_lease_key) { "object_storage_migrate:#{subject.model.class}:#{subject.model.id}" }
-
before do
- @uuid = Gitlab::ExclusiveLease.new(exclusive_lease_key, timeout: 1.hour.to_i).try_obtain
+ @uuid = Gitlab::ExclusiveLease.new(subject.exclusive_lease_key, timeout: 1.hour.to_i).try_obtain
end
it 'does not execute migrate!' do
expect(subject).not_to receive(:unsafe_migrate!)
- expect { migrate(to) }.to raise_error('exclusive lease already taken')
+ expect { migrate(to) }.to raise_error(ObjectStorage::ExclusiveLeaseTaken)
end
it 'does not execute use_file' do
expect(subject).not_to receive(:unsafe_use_file)
- expect { subject.use_file }.to raise_error('exclusive lease already taken')
+ expect { subject.use_file }.to raise_error(ObjectStorage::ExclusiveLeaseTaken)
end
after do
- Gitlab::ExclusiveLease.cancel(exclusive_lease_key, @uuid)
+ Gitlab::ExclusiveLease.cancel(subject.exclusive_lease_key, @uuid)
end
end
diff --git a/spec/support/shoulda/matchers/rails_shim.rb b/spec/support/shoulda/matchers/rails_shim.rb
new file mode 100644
index 00000000000..8d70598beb5
--- /dev/null
+++ b/spec/support/shoulda/matchers/rails_shim.rb
@@ -0,0 +1,27 @@
+# monkey patch which fixes serialization matcher in Rails 5
+# https://github.com/thoughtbot/shoulda-matchers/issues/913
+# This can be removed when a new version of shoulda-matchers
+# is released
+module Shoulda
+ module Matchers
+ class RailsShim
+ def self.serialized_attributes_for(model)
+ if defined?(::ActiveRecord::Type::Serialized)
+ # Rails 5+
+ serialized_columns = model.columns.select do |column|
+ model.type_for_attribute(column.name).is_a?(
+ ::ActiveRecord::Type::Serialized
+ )
+ end
+
+ serialized_columns.inject({}) do |hash, column| # rubocop:disable Style/EachWithObject
+ hash[column.name.to_s] = model.type_for_attribute(column.name).coder
+ hash
+ end
+ else
+ model.serialized_attributes
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index 1e507c0236e..4545226d78c 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -134,7 +134,9 @@ describe 'gitlab:gitaly namespace rake task' do
parsed_output = TomlRB.parse(expected_output)
config.each do |name, params|
- expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params.legacy_disk_path })
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ expect(parsed_output['storage']).to include({ 'name' => name, 'path' => params.legacy_disk_path })
+ end
end
end
end
diff --git a/spec/tasks/gitlab/shell_rake_spec.rb b/spec/tasks/gitlab/shell_rake_spec.rb
index 4a756c5742d..0ed5d3e27b9 100644
--- a/spec/tasks/gitlab/shell_rake_spec.rb
+++ b/spec/tasks/gitlab/shell_rake_spec.rb
@@ -7,11 +7,17 @@ describe 'gitlab:shell rake tasks' do
stub_warn_user_is_not_gitlab
end
+ after do
+ TestEnv.create_fake_git_hooks
+ end
+
describe 'install task' do
it 'invokes create_hooks task' do
expect(Rake::Task['gitlab:shell:create_hooks']).to receive(:invoke)
- storages = Gitlab.config.repositories.storages.values.map(&:legacy_disk_path)
+ storages = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Gitlab.config.repositories.storages.values.map(&:legacy_disk_path)
+ end
expect(Kernel).to receive(:system).with('bin/install', *storages).and_call_original
expect(Kernel).to receive(:system).with('bin/compile').and_call_original
diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb
index 35e451b2f9a..233076ad6fa 100644
--- a/spec/tasks/gitlab/storage_rake_spec.rb
+++ b/spec/tasks/gitlab/storage_rake_spec.rb
@@ -1,6 +1,6 @@
require 'rake_helper'
-describe 'gitlab:storage:*' do
+describe 'rake gitlab:storage:*' do
before do
Rake.application.rake_require 'tasks/gitlab/storage'
@@ -44,16 +44,18 @@ describe 'gitlab:storage:*' do
end
describe 'gitlab:storage:migrate_to_hashed' do
+ let(:task) { 'gitlab:storage:migrate_to_hashed' }
+
context '0 legacy projects' do
it 'does nothing' do
expect(StorageMigratorWorker).not_to receive(:perform_async)
- run_rake_task('gitlab:storage:migrate_to_hashed')
+ run_rake_task(task)
end
end
context '3 legacy projects' do
- let(:projects) { create_list(:project, 3, storage_version: 0) }
+ let(:projects) { create_list(:project, 3, :legacy_storage) }
context 'in batches of 1' do
before do
@@ -65,7 +67,7 @@ describe 'gitlab:storage:*' do
expect(StorageMigratorWorker).to receive(:perform_async).with(project.id, project.id)
end
- run_rake_task('gitlab:storage:migrate_to_hashed')
+ run_rake_task(task)
end
end
@@ -80,23 +82,48 @@ describe 'gitlab:storage:*' do
expect(StorageMigratorWorker).to receive(:perform_async).with(first, last)
end
- run_rake_task('gitlab:storage:migrate_to_hashed')
+ run_rake_task(task)
end
end
end
+
+ context 'with same id in range' do
+ it 'displays message when project cant be found' do
+ stub_env('ID_FROM', 99999)
+ stub_env('ID_TO', 99999)
+
+ expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=99999/).to_stdout
+ end
+
+ it 'displays a message when project exists but its already migrated' do
+ project = create(:project)
+ stub_env('ID_FROM', project.id)
+ stub_env('ID_TO', project.id)
+
+ expect { run_rake_task(task) }.to output(/There are no projects requiring storage migration with ID=#{project.id}/).to_stdout
+ end
+
+ it 'enqueues migration when project can be found' do
+ project = create(:project, :legacy_storage)
+ stub_env('ID_FROM', project.id)
+ stub_env('ID_TO', project.id)
+
+ expect { run_rake_task(task) }.to output(/Enqueueing storage migration .* \(ID=#{project.id}\)/).to_stdout
+ end
+ end
end
describe 'gitlab:storage:legacy_projects' do
it_behaves_like 'rake entities summary', 'projects', 'Legacy' do
let(:task) { 'gitlab:storage:legacy_projects' }
- let(:create_collection) { create_list(:project, 3, storage_version: 0) }
+ let(:create_collection) { create_list(:project, 3, :legacy_storage) }
end
end
describe 'gitlab:storage:list_legacy_projects' do
it_behaves_like 'rake listing entities', 'projects', 'Legacy' do
let(:task) { 'gitlab:storage:list_legacy_projects' }
- let(:create_collection) { create_list(:project, 3, storage_version: 0) }
+ let(:create_collection) { create_list(:project, 3, :legacy_storage) }
end
end
@@ -133,7 +160,7 @@ describe 'gitlab:storage:*' do
describe 'gitlab:storage:hashed_attachments' do
it_behaves_like 'rake entities summary', 'attachments', 'Hashed' do
let(:task) { 'gitlab:storage:hashed_attachments' }
- let(:project) { create(:project, storage_version: 2) }
+ let(:project) { create(:project) }
let(:create_collection) { create_list(:upload, 3, model: project) }
end
end
@@ -141,7 +168,7 @@ describe 'gitlab:storage:*' do
describe 'gitlab:storage:list_hashed_attachments' do
it_behaves_like 'rake listing entities', 'attachments', 'Hashed' do
let(:task) { 'gitlab:storage:list_hashed_attachments' }
- let(:project) { create(:project, storage_version: 2) }
+ let(:project) { create(:project) }
let(:create_collection) { create_list(:upload, 3, model: project) }
end
end
diff --git a/spec/uploaders/attachment_uploader_spec.rb b/spec/uploaders/attachment_uploader_spec.rb
index d302c14efb9..a9415854d25 100644
--- a/spec/uploaders/attachment_uploader_spec.rb
+++ b/spec/uploaders/attachment_uploader_spec.rb
@@ -26,7 +26,7 @@ describe AttachmentUploader do
describe "#migrate!" do
before do
- uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt')))
+ uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt')))
stub_uploads_object_storage
end
diff --git a/spec/uploaders/favicon_uploader_spec.rb b/spec/uploaders/favicon_uploader_spec.rb
new file mode 100644
index 00000000000..37deea8ab90
--- /dev/null
+++ b/spec/uploaders/favicon_uploader_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+RSpec.describe FaviconUploader do
+ include CarrierWave::Test::Matchers
+
+ let(:uploader) { described_class.new(build_stubbed(:user)) }
+
+ after do
+ uploader.remove!
+ end
+
+ def upload_fixture(filename)
+ fixture_file_upload("spec/fixtures/#{filename}")
+ end
+
+ context 'versions' do
+ before do
+ uploader.store!(upload_fixture('dk.png'))
+ end
+
+ it 'has the correct format' do
+ expect(uploader.favicon_main).to be_format('png')
+ end
+
+ it 'has the correct dimensions' do
+ expect(uploader.favicon_main).to have_dimensions(32, 32)
+ end
+ end
+end
diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb
index 68b7e24776d..de29d0c943f 100644
--- a/spec/uploaders/file_mover_spec.rb
+++ b/spec/uploaders/file_mover_spec.rb
@@ -2,7 +2,7 @@ require 'spec_helper'
describe FileMover do
let(:filename) { 'banana_sample.gif' }
- let(:file) { fixture_file_upload(Rails.root.join('spec', 'fixtures', filename)) }
+ let(:file) { fixture_file_upload(File.join('spec', 'fixtures', filename)) }
let(:temp_file_path) { File.join('uploads/-/system/temp', 'secret55', filename) }
let(:temp_description) do
diff --git a/spec/uploaders/file_uploader_spec.rb b/spec/uploaders/file_uploader_spec.rb
index db2810bbe1d..59013a02938 100644
--- a/spec/uploaders/file_uploader_spec.rb
+++ b/spec/uploaders/file_uploader_spec.rb
@@ -89,7 +89,7 @@ describe FileUploader do
describe "#migrate!" do
before do
- uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/dk.png')))
+ uploader.store!(fixture_file_upload('spec/fixtures/dk.png'))
stub_uploads_object_storage
end
diff --git a/spec/uploaders/gitlab_uploader_spec.rb b/spec/uploaders/gitlab_uploader_spec.rb
index 4fba122cce1..362f89424d4 100644
--- a/spec/uploaders/gitlab_uploader_spec.rb
+++ b/spec/uploaders/gitlab_uploader_spec.rb
@@ -62,7 +62,7 @@ describe GitlabUploader do
expect(FileUtils).to receive(:mv).with(anything, /^#{subject.work_dir}/).and_call_original
expect(FileUtils).to receive(:mv).with(/^#{subject.work_dir}/, /#{subject.cache_dir}/).and_call_original
- fixture = Rails.root.join('spec', 'fixtures', 'rails_sample.jpg')
+ fixture = File.join('spec', 'fixtures', 'rails_sample.jpg')
subject.cache!(fixture_file_upload(fixture))
expect(subject.file.path).to match(/#{subject.cache_dir}/)
diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb
index 42036d67f3d..026e4356ed6 100644
--- a/spec/uploaders/job_artifact_uploader_spec.rb
+++ b/spec/uploaders/job_artifact_uploader_spec.rb
@@ -29,8 +29,7 @@ describe JobArtifactUploader do
context 'when trace is stored in File storage' do
context 'when file exists' do
let(:file) do
- fixture_file_upload(
- Rails.root.join('spec/fixtures/trace/sample_trace'), 'text/plain')
+ fixture_file_upload('spec/fixtures/trace/sample_trace', 'text/plain')
end
before do
@@ -63,8 +62,7 @@ describe JobArtifactUploader do
context 'file is stored in valid local_path' do
let(:file) do
- fixture_file_upload(
- Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip')
+ fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip')
end
before do
@@ -81,7 +79,7 @@ describe JobArtifactUploader do
describe "#migrate!" do
before do
- uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/trace/sample_trace')))
+ uploader.store!(fixture_file_upload('spec/fixtures/trace/sample_trace'))
stub_artifacts_object_storage
end
diff --git a/spec/uploaders/legacy_artifact_uploader_spec.rb b/spec/uploaders/legacy_artifact_uploader_spec.rb
index eeb6fd90c9d..0589563b502 100644
--- a/spec/uploaders/legacy_artifact_uploader_spec.rb
+++ b/spec/uploaders/legacy_artifact_uploader_spec.rb
@@ -44,8 +44,7 @@ describe LegacyArtifactUploader do
context 'file is stored in valid path' do
let(:file) do
- fixture_file_upload(
- Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip')
+ fixture_file_upload('spec/fixtures/ci_build_artifacts.zip', 'application/zip')
end
before do
diff --git a/spec/uploaders/namespace_file_uploader_spec.rb b/spec/uploaders/namespace_file_uploader_spec.rb
index a8ba01d70b8..71fe2c353c0 100644
--- a/spec/uploaders/namespace_file_uploader_spec.rb
+++ b/spec/uploaders/namespace_file_uploader_spec.rb
@@ -28,7 +28,7 @@ describe NamespaceFileUploader do
describe "#migrate!" do
before do
- uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt')))
+ uploader.store!(fixture_file_upload(File.join('spec/fixtures/doc_sample.txt')))
stub_uploads_object_storage
end
diff --git a/spec/uploaders/object_storage_spec.rb b/spec/uploaders/object_storage_spec.rb
index 4503288e410..c7f5694ff43 100644
--- a/spec/uploaders/object_storage_spec.rb
+++ b/spec/uploaders/object_storage_spec.rb
@@ -321,7 +321,7 @@ describe ObjectStorage do
when_file_is_in_use do
expect(uploader).not_to receive(:unsafe_migrate!)
- expect { uploader.migrate!(described_class::Store::REMOTE) }.to raise_error('exclusive lease already taken')
+ expect { uploader.migrate!(described_class::Store::REMOTE) }.to raise_error(ObjectStorage::ExclusiveLeaseTaken)
end
end
@@ -329,7 +329,19 @@ describe ObjectStorage do
when_file_is_in_use do
expect(uploader).not_to receive(:unsafe_use_file)
- expect { uploader.use_file }.to raise_error('exclusive lease already taken')
+ expect { uploader.use_file }.to raise_error(ObjectStorage::ExclusiveLeaseTaken)
+ end
+ end
+
+ it 'can still migrate other files of the same model' do
+ uploader2 = uploader_class.new(object, :file)
+ uploader2.upload = create(:upload)
+ uploader.upload = create(:upload)
+
+ when_file_is_in_use do
+ expect(uploader2).to receive(:unsafe_migrate!)
+
+ uploader2.migrate!(described_class::Store::REMOTE)
end
end
end
@@ -571,7 +583,7 @@ describe ObjectStorage do
context 'when local file is used' do
context 'when valid file is used' do
let(:uploaded_file) do
- fixture_file_upload(Rails.root + 'spec/fixtures/rails_sample.jpg', 'image/jpg')
+ fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
end
it "properly caches the file" do
diff --git a/spec/uploaders/personal_file_uploader_spec.rb b/spec/uploaders/personal_file_uploader_spec.rb
index c70521d90dc..7700b14ce6b 100644
--- a/spec/uploaders/personal_file_uploader_spec.rb
+++ b/spec/uploaders/personal_file_uploader_spec.rb
@@ -45,7 +45,7 @@ describe PersonalFileUploader do
describe "#migrate!" do
before do
- uploader.store!(fixture_file_upload(Rails.root.join('spec/fixtures/doc_sample.txt')))
+ uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
stub_uploads_object_storage
end
diff --git a/spec/uploaders/records_uploads_spec.rb b/spec/uploaders/records_uploads_spec.rb
index 9a3e5d83e01..3592a11360d 100644
--- a/spec/uploaders/records_uploads_spec.rb
+++ b/spec/uploaders/records_uploads_spec.rb
@@ -16,7 +16,7 @@ describe RecordsUploads do
end
def upload_fixture(filename)
- fixture_file_upload(Rails.root.join('spec', 'fixtures', filename))
+ fixture_file_upload(File.join('spec', 'fixtures', filename))
end
describe 'callbacks' do
diff --git a/spec/uploaders/uploader_helper_spec.rb b/spec/uploaders/uploader_helper_spec.rb
index c47f09adb6d..33da93cc9d0 100644
--- a/spec/uploaders/uploader_helper_spec.rb
+++ b/spec/uploaders/uploader_helper_spec.rb
@@ -12,7 +12,7 @@ describe UploaderHelper do
end
def upload_fixture(filename)
- fixture_file_upload(Rails.root.join('spec', 'fixtures', filename))
+ fixture_file_upload(File.join('spec', 'fixtures', filename))
end
describe '#image_or_video?' do
diff --git a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
index aed62f97448..da490cb02af 100644
--- a/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
+++ b/spec/uploaders/workers/object_storage/migrate_uploads_worker_spec.rb
@@ -11,6 +11,12 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
let(:uploads) { Upload.all }
let(:to_store) { ObjectStorage::Store::REMOTE }
+ def perform(uploads)
+ described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, to_store)
+ rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures
+ # swallow
+ end
+
shared_examples "uploads migration worker" do
describe '.enqueue!' do
def enqueue!
@@ -69,12 +75,6 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
end
describe '#perform' do
- def perform
- described_class.new.perform(uploads.ids, model_class.to_s, mounted_as, to_store)
- rescue ObjectStorage::MigrateUploadsWorker::Report::MigrationFailures
- # swallow
- end
-
shared_examples 'outputs correctly' do |success: 0, failures: 0|
total = success + failures
@@ -82,7 +82,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
it 'outputs the reports' do
expect(Rails.logger).to receive(:info).with(%r{Migrated #{success}/#{total} files})
- perform
+ perform(uploads)
end
end
@@ -90,7 +90,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
it 'outputs upload failures' do
expect(Rails.logger).to receive(:warn).with(/Error .* I am a teapot/)
- perform
+ perform(uploads)
end
end
end
@@ -98,7 +98,7 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
it_behaves_like 'outputs correctly', success: 10
it 'migrates files' do
- perform
+ perform(uploads)
expect(Upload.where(store: ObjectStorage::Store::LOCAL).count).to eq(0)
end
@@ -123,6 +123,17 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
end
it_behaves_like "uploads migration worker"
+
+ describe "limits N+1 queries" do
+ it "to N*5" do
+ query_count = ActiveRecord::QueryRecorder.new { perform(uploads) }
+
+ more_projects = create_list(:project, 3, :with_avatar)
+
+ expected_queries_per_migration = 5 * more_projects.count
+ expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration)
+ end
+ end
end
context "for FileUploader" do
@@ -130,15 +141,29 @@ describe ObjectStorage::MigrateUploadsWorker, :sidekiq do
let(:secret) { SecureRandom.hex }
let(:mounted_as) { nil }
+ def upload_file(project)
+ uploader = FileUploader.new(project)
+ uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
+ end
+
before do
stub_uploads_object_storage(FileUploader)
- projects.map do |project|
- uploader = FileUploader.new(project)
- uploader.store!(fixture_file_upload('spec/fixtures/doc_sample.txt'))
- end
+ projects.map(&method(:upload_file))
end
it_behaves_like "uploads migration worker"
+
+ describe "limits N+1 queries" do
+ it "to N*5" do
+ query_count = ActiveRecord::QueryRecorder.new { perform(uploads) }
+
+ more_projects = create_list(:project, 3)
+ more_projects.map(&method(:upload_file))
+
+ expected_queries_per_migration = 5 * more_projects.count
+ expect { perform(Upload.all) }.not_to exceed_query_limit(query_count).with_threshold(expected_queries_per_migration)
+ end
+ end
end
end
diff --git a/spec/validators/url_validator_spec.rb b/spec/validators/url_validator_spec.rb
index 2d719263fc8..93fe013d11c 100644
--- a/spec/validators/url_validator_spec.rb
+++ b/spec/validators/url_validator_spec.rb
@@ -50,13 +50,56 @@ describe UrlValidator do
end
end
- context 'when ports is set' do
- let(:validator) { described_class.new(attributes: [:link_url], ports: [443]) }
+ context 'when ports is' do
+ let(:validator) { described_class.new(attributes: [:link_url], ports: ports) }
- it 'blocks urls with a different port' do
- subject
+ context 'empty' do
+ let(:ports) { [] }
- expect(badge.errors.empty?).to be false
+ it 'does not block any port' do
+ subject
+
+ expect(badge.errors.empty?).to be true
+ end
+ end
+
+ context 'set' do
+ let(:ports) { [443] }
+
+ it 'blocks urls with a different port' do
+ subject
+
+ expect(badge.errors.empty?).to be false
+ end
+ end
+ end
+
+ context 'when enforce_user is' do
+ let(:url) { 'http://$user@example.com'}
+ let(:validator) { described_class.new(attributes: [:link_url], enforce_user: enforce_user) }
+
+ context 'true' do
+ let(:enforce_user) { true }
+
+ it 'checks user format' do
+ badge.link_url = url
+
+ subject
+
+ expect(badge.errors.empty?).to be false
+ end
+ end
+
+ context 'false (default)' do
+ let(:enforce_user) { false }
+
+ it 'does not check user format' do
+ badge.link_url = url
+
+ subject
+
+ expect(badge.errors.empty?).to be true
+ end
end
end
end
diff --git a/spec/views/errors/access_denied.html.haml_spec.rb b/spec/views/errors/access_denied.html.haml_spec.rb
new file mode 100644
index 00000000000..bde2f6f0169
--- /dev/null
+++ b/spec/views/errors/access_denied.html.haml_spec.rb
@@ -0,0 +1,7 @@
+require 'spec_helper'
+
+describe 'errors/access_denied' do
+ it 'does not fail to render when there is no message provided' do
+ expect { render }.not_to raise_error
+ end
+end
diff --git a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
index d15391911c1..cb1b9e6f5fb 100644
--- a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
+++ b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
@@ -12,7 +12,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do
it 'shows warning message' do
render
- expect(rendered).to have_css('.settings-message')
+ expect(rendered).to have_css('.auto-devops-warning-message')
expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name and a')
expect(rendered).to have_link('Kubernetes cluster')
end
@@ -26,7 +26,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do
it 'shows warning message' do
render
- expect(rendered).to have_css('.settings-message')
+ expect(rendered).to have_css('.auto-devops-warning-message')
expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a')
expect(rendered).to have_link('Kubernetes cluster')
end
@@ -42,7 +42,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do
it 'shows warning message' do
render
- expect(rendered).to have_css('.settings-message')
+ expect(rendered).to have_css('.auto-devops-warning-message')
expect(rendered).to have_text('Auto Review Apps and Auto Deploy need a domain name to work correctly.')
end
end
@@ -55,7 +55,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do
it 'does not show warning message' do
render
- expect(rendered).not_to have_css('.settings-message')
+ expect(rendered).not_to have_css('.auto-devops-warning-message')
end
end
end
diff --git a/spec/workers/git_garbage_collect_worker_spec.rb b/spec/workers/git_garbage_collect_worker_spec.rb
index f44b4edc305..e39dec556fc 100644
--- a/spec/workers/git_garbage_collect_worker_spec.rb
+++ b/spec/workers/git_garbage_collect_worker_spec.rb
@@ -11,36 +11,63 @@ describe GitGarbageCollectWorker do
subject { described_class.new }
describe "#perform" do
- shared_examples 'flushing ref caches' do |gitaly|
- context 'with active lease_uuid' do
+ context 'with active lease_uuid' do
+ before do
+ allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
+ end
+
+ it "flushes ref caches when the task if 'gc'" do
+ expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
+ .and_return(nil)
+ expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
+ expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
+ expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
+ expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
+
+ subject.perform(project.id, :gc, lease_key, lease_uuid)
+ end
+ end
+
+ context 'with different lease than the active one' do
+ before do
+ allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid)
+ end
+
+ it 'returns silently' do
+ expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original
+ expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
+ expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
+
+ subject.perform(project.id, :gc, lease_key, lease_uuid)
+ end
+ end
+
+ context 'with no active lease' do
+ before do
+ allow(subject).to receive(:get_lease_uuid).and_return(false)
+ end
+
+ context 'when is able to get the lease' do
before do
- allow(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
+ allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid)
end
it "flushes ref caches when the task if 'gc'" do
- expect(subject).to receive(:renew_lease).with(lease_key, lease_uuid).and_call_original
- expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
-
- if gitaly
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
- .and_return(nil)
- else
- expect(Gitlab::Popen).to receive(:popen)
- .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
- end
-
+ expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
+ .and_return(nil)
expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
- subject.perform(project.id, :gc, lease_key, lease_uuid)
+ subject.perform(project.id)
end
end
- context 'with different lease than the active one' do
+ context 'when no lease can be obtained' do
before do
- allow(subject).to receive(:get_lease_uuid).and_return(SecureRandom.uuid)
+ expect(subject).to receive(:try_obtain_lease).and_return(false)
end
it 'returns silently' do
@@ -49,65 +76,11 @@ describe GitGarbageCollectWorker do
expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
- subject.perform(project.id, :gc, lease_key, lease_uuid)
- end
- end
-
- context 'with no active lease' do
- before do
- allow(subject).to receive(:get_lease_uuid).and_return(false)
- end
-
- context 'when is able to get the lease' do
- before do
- allow(subject).to receive(:try_obtain_lease).and_return(SecureRandom.uuid)
- end
-
- it "flushes ref caches when the task if 'gc'" do
- expect(subject).to receive(:command).with(:gc).and_return([:the, :command])
-
- if gitaly
- expect_any_instance_of(Gitlab::GitalyClient::RepositoryService).to receive(:garbage_collect)
- .and_return(nil)
- else
- expect(Gitlab::Popen).to receive(:popen)
- .with([:the, :command], project.repository.path_to_repo).and_return(["", 0])
- end
-
- expect_any_instance_of(Repository).to receive(:after_create_branch).and_call_original
- expect_any_instance_of(Repository).to receive(:branch_names).and_call_original
- expect_any_instance_of(Repository).to receive(:has_visible_content?).and_call_original
- expect_any_instance_of(Gitlab::Git::Repository).to receive(:has_visible_content?).and_call_original
-
- subject.perform(project.id)
- end
- end
-
- context 'when no lease can be obtained' do
- before do
- expect(subject).to receive(:try_obtain_lease).and_return(false)
- end
-
- it 'returns silently' do
- expect(subject).not_to receive(:command)
- expect_any_instance_of(Repository).not_to receive(:after_create_branch).and_call_original
- expect_any_instance_of(Repository).not_to receive(:branch_names).and_call_original
- expect_any_instance_of(Repository).not_to receive(:has_visible_content?).and_call_original
-
- subject.perform(project.id)
- end
+ subject.perform(project.id)
end
end
end
- context "with Gitaly turned on" do
- it_should_behave_like 'flushing ref caches', true
- end
-
- context "with Gitaly turned off", :disable_gitaly do
- it_should_behave_like 'flushing ref caches', false
- end
-
context "repack_full" do
before do
expect(subject).to receive(:get_lease_uuid).and_return(lease_uuid)
@@ -218,7 +191,9 @@ describe GitGarbageCollectWorker do
# Create a new commit on a random new branch
def create_objects(project)
- rugged = project.repository.rugged
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
old_commit = rugged.branches.first.target
new_commit_sha = Rugged::Commit.create(
rugged,
@@ -237,7 +212,9 @@ describe GitGarbageCollectWorker do
end
def packs(project)
- Dir["#{project.repository.path_to_repo}/objects/pack/*.pack"]
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ Dir["#{project.repository.path_to_repo}/objects/pack/*.pack"]
+ end
end
def packed_refs(project)
diff --git a/spec/workers/project_destroy_worker_spec.rb b/spec/workers/project_destroy_worker_spec.rb
index f19c9dff941..42e1d86e3bb 100644
--- a/spec/workers/project_destroy_worker_spec.rb
+++ b/spec/workers/project_destroy_worker_spec.rb
@@ -2,7 +2,11 @@ require 'spec_helper'
describe ProjectDestroyWorker do
let(:project) { create(:project, :repository, pending_delete: true) }
- let(:path) { project.repository.path_to_repo }
+ let(:path) do
+ Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.path_to_repo
+ end
+ end
subject { described_class.new }
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index 4b3c1736ea0..5d83397e8df 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -55,10 +55,15 @@ describe RepositoryForkWorker do
it 'flushes various caches' do
expect_fork_repository.and_return(true)
- expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
+ # Works around https://github.com/rspec/rspec-mocks/issues/910
+ expect(Project).to receive(:find).with(fork_project.id).and_return(fork_project)
+ expect(fork_project.repository).to receive(:expire_emptiness_caches)
.and_call_original
-
- expect_any_instance_of(Repository).to receive(:expire_exists_cache)
+ expect(fork_project.repository).to receive(:expire_exists_cache)
+ .and_call_original
+ expect(fork_project.wiki.repository).to receive(:expire_emptiness_caches)
+ .and_call_original
+ expect(fork_project.wiki.repository).to receive(:expire_exists_cache)
.and_call_original
perform!
@@ -89,6 +94,9 @@ describe RepositoryForkWorker do
it_behaves_like 'RepositoryForkWorker performing'
it 'logs a message about forking with old-style arguments' do
+ allow(subject).to receive(:gitlab_shell).and_return(shell)
+ expect(shell).to receive(:fork_repository) { true }
+
allow(Rails.logger).to receive(:info).with(anything) # To compensate for other logs
expect(Rails.logger).to receive(:info).with("Project #{fork_project.id} is being forked using old-style arguments.")
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 84d1b38ef19..f0884ad0aff 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -22,8 +22,11 @@ describe RepositoryImportWorker do
expect_any_instance_of(Projects::ImportService).to receive(:execute)
.and_return({ status: :ok })
- expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
- expect_any_instance_of(Project).to receive(:import_finish)
+ # Works around https://github.com/rspec/rspec-mocks/issues/910
+ expect(Project).to receive(:find).with(project.id).and_return(project)
+ expect(project.repository).to receive(:expire_emptiness_caches)
+ expect(project.wiki.repository).to receive(:expire_emptiness_caches)
+ expect(project).to receive(:import_finish)
subject.perform(project.id)
end
@@ -34,9 +37,11 @@ describe RepositoryImportWorker do
expect_any_instance_of(Projects::ImportService).to receive(:execute)
.and_return({ status: :ok })
- expect_any_instance_of(Project).to receive(:after_import).and_call_original
- expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
- expect_any_instance_of(Project).to receive(:import_finish)
+ # Works around https://github.com/rspec/rspec-mocks/issues/910
+ expect(Project).to receive(:find).with(project.id).and_return(project)
+ expect(project.repository).to receive(:expire_emptiness_caches)
+ expect(project.wiki.repository).to receive(:expire_emptiness_caches)
+ expect(project).to receive(:import_finish)
subject.perform(project.id)
end
diff --git a/spec/workers/repository_remove_remote_worker_spec.rb b/spec/workers/repository_remove_remote_worker_spec.rb
index f22d7c1d073..5968c5da3c9 100644
--- a/spec/workers/repository_remove_remote_worker_spec.rb
+++ b/spec/workers/repository_remove_remote_worker_spec.rb
@@ -44,7 +44,9 @@ describe RepositoryRemoveRemoteWorker do
end
def create_remote_branch(remote_name, branch_name, target)
- rugged = project.repository.rugged
+ rugged = Gitlab::GitalyClient::StorageSettings.allow_disk_access do
+ project.repository.rugged
+ end
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
end
end
diff --git a/spec/workers/storage_migrator_worker_spec.rb b/spec/workers/storage_migrator_worker_spec.rb
index ff625164142..815432aacce 100644
--- a/spec/workers/storage_migrator_worker_spec.rb
+++ b/spec/workers/storage_migrator_worker_spec.rb
@@ -2,29 +2,24 @@ require 'spec_helper'
describe StorageMigratorWorker do
subject(:worker) { described_class.new }
- let(:projects) { create_list(:project, 2, :legacy_storage) }
+ let(:projects) { create_list(:project, 2, :legacy_storage, :empty_repo) }
+ let(:ids) { projects.map(&:id) }
describe '#perform' do
- let(:ids) { projects.map(&:id) }
+ it 'delegates to MigratorService' do
+ expect_any_instance_of(Gitlab::HashedStorage::Migrator).to receive(:bulk_migrate).with(5, 10)
- it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do
- expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice
-
- worker.perform(ids.min, ids.max)
+ worker.perform(5, 10)
end
- it 'sets projects as read only' do
- allow(ProjectMigrateHashedStorageWorker).to receive(:perform_async).twice
- worker.perform(ids.min, ids.max)
+ it 'migrates projects in the specified range' do
+ Sidekiq::Testing.inline! do
+ worker.perform(ids.min, ids.max)
+ end
projects.each do |project|
- expect(project.reload.repository_read_only?).to be_truthy
+ expect(project.reload.hashed_storage?(:attachments)).to be_truthy
end
end
-
- it 'rescues and log exceptions' do
- allow_any_instance_of(Project).to receive(:migrate_to_hashed_storage!).and_raise(StandardError)
- expect { worker.perform(ids.min, ids.max) }.not_to raise_error
- end
end
end
diff --git a/vendor/gitignore/Dart.gitignore b/vendor/gitignore/Dart.gitignore
index 7bf00e82cc9..dbef116d224 100644
--- a/vendor/gitignore/Dart.gitignore
+++ b/vendor/gitignore/Dart.gitignore
@@ -3,7 +3,6 @@
# Files and directories created by pub
.dart_tool/
.packages
-.pub/
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
@@ -11,3 +10,12 @@ pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
+
+# Avoid committing generated Javascript files:
+*.dart.js
+*.info.json # Produced by the --dump-info flag.
+*.js # When generated by dart2js. Don't specify *.js if your
+ # project includes source files written in JavaScript.
+*.js_
+*.js.deps
+*.js.map
diff --git a/vendor/gitignore/Global/JetBrains.gitignore b/vendor/gitignore/Global/JetBrains.gitignore
index b09cb3dbc04..4d5117a1d9d 100644
--- a/vendor/gitignore/Global/JetBrains.gitignore
+++ b/vendor/gitignore/Global/JetBrains.gitignore
@@ -14,6 +14,7 @@
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
diff --git a/vendor/gitignore/Node.gitignore b/vendor/gitignore/Node.gitignore
index ad46b30886f..4454ba1b5bf 100644
--- a/vendor/gitignore/Node.gitignore
+++ b/vendor/gitignore/Node.gitignore
@@ -59,3 +59,9 @@ typings/
# next.js build output
.next
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless
diff --git a/vendor/gitignore/Sass.gitignore b/vendor/gitignore/Sass.gitignore
index 486b32ce90c..159f515170b 100644
--- a/vendor/gitignore/Sass.gitignore
+++ b/vendor/gitignore/Sass.gitignore
@@ -1,2 +1,4 @@
.sass-cache/
*.css.map
+*.sass.map
+*.scss.map
diff --git a/vendor/gitignore/TeX.gitignore b/vendor/gitignore/TeX.gitignore
index e6598ba1727..3d12d3f90ad 100644
--- a/vendor/gitignore/TeX.gitignore
+++ b/vendor/gitignore/TeX.gitignore
@@ -40,6 +40,10 @@
*.synctex.gz(busy)
*.pdfsync
+## Build tool directories for auxiliary files
+# latexrun
+latex.out/
+
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
diff --git a/vendor/gitignore/Terraform.gitignore b/vendor/gitignore/Terraform.gitignore
index 1fef4ab91e1..d9397e2d7d9 100644
--- a/vendor/gitignore/Terraform.gitignore
+++ b/vendor/gitignore/Terraform.gitignore
@@ -1,9 +1,15 @@
-# Local .terraform directories
+# Local .terraform directories
**/.terraform/*
# .tfstate files
*.tfstate
*.tfstate.*
-# .tfvars files
-*.tfvars
+# Crash log files
+crash.log
+
+# Ignore any .tfvars files that are generated automatically for each Terraform run. Most
+# .tfvars files are managed as part of configuration and so should be included in
+# version control.
+#
+# example.tfvars
diff --git a/vendor/gitignore/VisualStudio.gitignore b/vendor/gitignore/VisualStudio.gitignore
index 3e759b75bf4..1e9abf78d69 100644
--- a/vendor/gitignore/VisualStudio.gitignore
+++ b/vendor/gitignore/VisualStudio.gitignore
@@ -52,7 +52,6 @@ BenchmarkDotNet.Artifacts/
project.lock.json
project.fragment.lock.json
artifacts/
-**/Properties/launchSettings.json
# StyleCop
StyleCopReport.xml
diff --git a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
index 27e8245185e..0d58a00482a 100644
--- a/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
+++ b/vendor/gitlab-ci-yml/Auto-DevOps.gitlab-ci.yml
@@ -10,6 +10,7 @@
# Test jobs may be disabled by setting environment variables:
# * test: TEST_DISABLED
# * code_quality: CODE_QUALITY_DISABLED
+# * license_management: LICENSE_MANAGEMENT_DISABLED
# * performance: PERFORMANCE_DISABLED
# * sast: SAST_DISABLED
# * dependency_scanning: DEPENDENCY_SCANNING_DISABLED
@@ -108,6 +109,22 @@ code_quality:
variables:
- $CODE_QUALITY_DISABLED
+license_management:
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - setup_docker
+ - license_management
+ artifacts:
+ paths: [gl-license-management-report.json]
+ except:
+ variables:
+ - $LICENSE_MANAGEMENT_DISABLED
+
performance:
stage: performance
image: docker:stable
@@ -462,6 +479,18 @@ rollout 100%:
"registry.gitlab.com/gitlab-org/security-products/codequality:$SP_VERSION" /code
}
+ function license_management() {
+ if echo $GITLAB_FEATURES |grep license_management > /dev/null ; then
+ # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable"
+ LICENSE_MANAGEMENT_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
+
+ docker run --volume "$PWD:/code" \
+ "registry.gitlab.com/gitlab-org/security-products/license-management:$LICENSE_MANAGEMENT_VERSION" analyze /code
+ else
+ echo "License management is not available in your subscription"
+ fi
+ }
+
function sast() {
case "$CI_SERVER_VERSION" in
*-ee)
diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml
deleted file mode 100644
index 6e5fe97cf6d..00000000000
--- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes-with-canary.gitlab-ci.yml
+++ /dev/null
@@ -1,87 +0,0 @@
-# This template has been DEPRECATED. Consider using Auto DevOps instead:
-# https://docs.gitlab.com/ee/topics/autodevops
-
-# Explanation on the scripts:
-# https://gitlab.com/gitlab-examples/kubernetes-deploy/blob/master/README.md
-image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
-
-variables:
- # Application deployment domain
- KUBE_DOMAIN: domain.example.com
-
-stages:
- - build
- - test
- - review
- - staging
- - canary
- - production
- - cleanup
-
-build:
- stage: build
- script:
- - command build
- only:
- - branches
-
-canary:
- stage: canary
- script:
- - command canary
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN
- when: manual
- only:
- - master
-
-production:
- stage: production
- script:
- - command deploy
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN
- when: manual
- only:
- - master
-
-staging:
- stage: staging
- script:
- - command deploy
- environment:
- name: staging
- url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_DOMAIN
- only:
- - master
-
-review:
- stage: review
- script:
- - command deploy
- environment:
- name: review/$CI_COMMIT_REF_NAME
- url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
- on_stop: stop_review
- only:
- - branches
- except:
- - master
-
-stop_review:
- stage: cleanup
- variables:
- GIT_STRATEGY: none
- script:
- - command destroy
- environment:
- name: review/$CI_COMMIT_REF_NAME
- action: stop
- when: manual
- allow_failure: true
- only:
- - branches
- except:
- - master
diff --git a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
deleted file mode 100644
index 019a4d4cd7d..00000000000
--- a/vendor/gitlab-ci-yml/autodeploy/Kubernetes.gitlab-ci.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-# This template has been DEPRECATED. Consider using Auto DevOps instead:
-# https://docs.gitlab.com/ee/topics/autodevops
-
-# Explanation on the scripts:
-# https://gitlab.com/gitlab-examples/kubernetes-deploy/blob/master/README.md
-image: registry.gitlab.com/gitlab-examples/kubernetes-deploy
-
-variables:
- # Application deployment domain
- KUBE_DOMAIN: domain.example.com
-
-stages:
- - build
- - test
- - review
- - staging
- - production
- - cleanup
-
-build:
- stage: build
- script:
- - command build
- only:
- - branches
-
-production:
- stage: production
- script:
- - command deploy
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN
- when: manual
- only:
- - master
-
-staging:
- stage: staging
- script:
- - command deploy
- environment:
- name: staging
- url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_DOMAIN
- only:
- - master
-
-review:
- stage: review
- script:
- - command deploy
- environment:
- name: review/$CI_COMMIT_REF_NAME
- url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
- on_stop: stop_review
- only:
- - branches
- except:
- - master
-
-stop_review:
- stage: cleanup
- variables:
- GIT_STRATEGY: none
- script:
- - command destroy
- environment:
- name: review/$CI_COMMIT_REF_NAME
- action: stop
- when: manual
- only:
- - branches
- except:
- - master
diff --git a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml b/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml
deleted file mode 100644
index 60a9430a839..00000000000
--- a/vendor/gitlab-ci-yml/autodeploy/OpenShift.gitlab-ci.yml
+++ /dev/null
@@ -1,74 +0,0 @@
-# This template has been DEPRECATED. Consider using Auto DevOps instead:
-# https://docs.gitlab.com/ee/topics/autodevops
-
-# Explanation on the scripts:
-# https://gitlab.com/gitlab-examples/openshift-deploy/blob/master/README.md
-image: registry.gitlab.com/gitlab-examples/openshift-deploy
-
-variables:
- # Application deployment domain
- KUBE_DOMAIN: domain.example.com
-
-stages:
- - build
- - test
- - review
- - staging
- - production
- - cleanup
-
-build:
- stage: build
- script:
- - command build
- only:
- - branches
-
-production:
- stage: production
- script:
- - command deploy
- environment:
- name: production
- url: http://$CI_PROJECT_PATH_SLUG.$KUBE_DOMAIN
- when: manual
- only:
- - master
-
-staging:
- stage: staging
- script:
- - command deploy
- environment:
- name: staging
- url: http://$CI_PROJECT_PATH_SLUG-staging.$KUBE_DOMAIN
- only:
- - master
-
-review:
- stage: review
- script:
- - command deploy
- environment:
- name: review/$CI_COMMIT_REF_NAME
- url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_DOMAIN
- on_stop: stop_review
- only:
- - branches
- except:
- - master
-
-stop_review:
- stage: cleanup
- variables:
- GIT_STRATEGY: none
- script:
- - command destroy
- environment:
- name: review/$CI_COMMIT_REF_NAME
- action: stop
- when: manual
- only:
- - branches
- except:
- - master
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index 07861631a07..dada0da0b31 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -1,20 +1,41 @@
-@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.20.0,SEE LICENSE IN LICENSE
-@mrmlnc/readdir-enhanced,2.2.1,MIT
+@babel/code-frame,7.0.0-beta.44,MIT
+@babel/generator,7.0.0-beta.44,MIT
+@babel/helper-function-name,7.0.0-beta.44,MIT
+@babel/helper-get-function-arity,7.0.0-beta.44,MIT
+@babel/helper-split-export-declaration,7.0.0-beta.44,MIT
+@babel/highlight,7.0.0-beta.44,MIT
+@babel/template,7.0.0-beta.44,MIT
+@babel/traverse,7.0.0-beta.44,MIT
+@babel/types,7.0.0-beta.44,MIT
+@gitlab-org/gitlab-svgs,1.23.0,SEE LICENSE IN LICENSE
@sindresorhus/is,0.7.0,MIT
@types/jquery,2.0.48,MIT
+@vue/component-compiler-utils,1.2.1,MIT
+@webassemblyjs/ast,1.5.10,MIT
+@webassemblyjs/floating-point-hex-parser,1.5.10,MIT
+@webassemblyjs/helper-api-error,1.5.10,MIT
+@webassemblyjs/helper-buffer,1.5.10,MIT
+@webassemblyjs/helper-code-frame,1.5.10,MIT
+@webassemblyjs/helper-fsm,1.5.10,ISC
+@webassemblyjs/helper-module-context,1.5.10,MIT
+@webassemblyjs/helper-wasm-bytecode,1.5.10,MIT
+@webassemblyjs/helper-wasm-section,1.5.10,MIT
+@webassemblyjs/ieee754,1.5.10,Unknown
+@webassemblyjs/leb128,1.5.10,Apache 2.0
+@webassemblyjs/utf8,1.5.10,MIT
+@webassemblyjs/wasm-edit,1.5.10,MIT
+@webassemblyjs/wasm-gen,1.5.10,MIT
+@webassemblyjs/wasm-opt,1.5.10,MIT
+@webassemblyjs/wasm-parser,1.5.10,MIT
+@webassemblyjs/wast-parser,1.5.10,MIT
+@webassemblyjs/wast-printer,1.5.10,MIT
RedCloth,4.3.2,MIT
abbrev,1.0.9,ISC
abbrev,1.1.1,ISC
accepts,1.3.4,MIT
ace-rails-ap,4.1.2,MIT
acorn,3.3.0,MIT
-acorn,5.4.1,MIT
+acorn,5.5.3,MIT
acorn-dynamic-import,3.0.0,MIT
acorn-jsx,3.0.1,MIT
actionmailer,4.2.10,MIT
@@ -33,7 +54,7 @@ agent-base,2.1.1,MIT
ajv,4.11.8,MIT
ajv,5.5.2,MIT
ajv,6.1.1,MIT
-ajv-keywords,1.5.1,MIT
+ajv-keywords,2.1.1,MIT
ajv-keywords,3.1.0,MIT
akismet,2.0.0,MIT
align-text,0.1.4,MIT
@@ -42,15 +63,12 @@ alphanum-sort,1.0.2,MIT
amdefine,1.0.1,BSD-3-Clause OR MIT
amqplib,0.5.2,MIT
ansi-align,2.0.0,ISC
-ansi-escapes,1.4.0,MIT
ansi-escapes,3.0.0,MIT
ansi-html,0.0.7,Apache 2.0
ansi-regex,2.1.1,MIT
ansi-regex,3.0.0,MIT
-ansi-styles,1.0.0,MIT
ansi-styles,2.2.1,MIT
ansi-styles,3.2.1,MIT
-any-observable,0.2.0,MIT
anymatch,1.3.2,ISC
anymatch,2.0.0,ISC
append-transform,0.4.0,MIT
@@ -62,7 +80,6 @@ arr-diff,2.0.0,MIT
arr-diff,4.0.0,MIT
arr-flatten,1.1.0,MIT
arr-union,3.1.0,MIT
-array-differ,1.0.0,MIT
array-find,1.0.0,MIT
array-find-index,1.0.2,MIT
array-flatten,1.1.1,MIT
@@ -85,12 +102,9 @@ assert-plus,0.2.0,MIT
assert-plus,1.0.0,MIT
asset_sync,2.4.0,MIT
assign-symbols,1.0.0,MIT
-ast-types,0.10.1,MIT
-ast-types,0.11.1,MIT
ast-types,0.11.3,MIT
async,1.5.2,MIT
async,2.1.5,MIT
-async,2.4.1,MIT
async,2.6.0,MIT
async-each,1.0.1,MIT
async-limiter,1.0.0,MIT
@@ -100,7 +114,6 @@ atomic,1.1.99,Apache 2.0
attr_encrypted,3.1.0,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
@@ -108,10 +121,10 @@ 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
+axios-mock-adapter,1.15.0,MIT
babel-code-frame,6.26.0,MIT
babel-core,6.26.3,MIT
-babel-eslint,8.0.2,MIT
+babel-eslint,8.2.3,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
@@ -134,18 +147,14 @@ babel-plugin-istanbul,4.1.6,New BSD
babel-plugin-rewire,1.1.0,ISC
babel-plugin-syntax-async-functions,6.13.0,MIT
babel-plugin-syntax-async-generators,6.13.0,MIT
-babel-plugin-syntax-class-constructor-call,6.18.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-export-extensions,6.13.0,MIT
-babel-plugin-syntax-flow,6.18.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-constructor-call,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
@@ -172,8 +181,6 @@ 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-export-extensions,6.22.0,MIT
-babel-plugin-transform-flow-strip-types,6.22.0,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
@@ -181,7 +188,6 @@ 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-1,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
@@ -191,7 +197,6 @@ 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
babylon,7.0.0-beta.44,MIT
backo2,1.0.2,MIT
balanced-match,0.4.2,MIT
@@ -210,9 +215,8 @@ better-assert,1.0.2,MIT
bfj-node4,5.2.1,MIT
big.js,3.1.3,MIT
binary-extensions,1.11.0,MIT
-binaryextensions,2.1.1,MIT
bindata,2.4.3,ruby
-bitsyntax,0.0.4,UNKNOWN
+bitsyntax,0.0.4,Unknown
bl,1.1.2,MIT
blackst0ne-mermaid,7.1.0-fixed,MIT
blob,0.0.4,MIT*
@@ -224,7 +228,7 @@ 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,4.1.1,MIT
bootstrap_form,2.7.0,MIT
boxen,1.3.0,MIT
brace-expansion,1.1.11,MIT
@@ -238,9 +242,10 @@ browserify-cipher,1.0.0,MIT
browserify-des,1.0.0,MIT
browserify-rsa,4.0.1,MIT
browserify-sign,4.0.4,ISC
-browserify-zlib,0.1.4,MIT
+browserify-zlib,0.2.0,MIT
browserslist,1.7.7,MIT
buffer,4.9.1,MIT
+buffer-from,1.0.0,MIT
buffer-indexof,1.1.0,MIT
buffer-more-ints,0.0.2,MIT
buffer-xor,1.0.3,MIT
@@ -252,8 +257,8 @@ bytes,2.5.0,MIT
bytes,3.0.0,MIT
cacache,10.0.4,ISC
cache-base,1.0.1,MIT
+cache-loader,1.2.2,MIT
cacheable-request,2.1.4,MIT
-call-me-maybe,1.0.1,MIT
caller-path,0.1.0,MIT
callsite,1.0.0,MIT*
callsites,0.2.0,MIT
@@ -269,9 +274,7 @@ 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,0.4.0,MIT
chalk,1.1.3,MIT
-chalk,2.4.0,MIT
chalk,2.4.1,MIT
chardet,0.4.2,MIT
charenc,0.0.2,New BSD
@@ -293,23 +296,13 @@ clap,1.1.3,MIT
class-utils,0.3.6,MIT
classlist-polyfill,1.2.0,Unlicense
cli-boxes,1.0.0,MIT
-cli-cursor,1.0.2,MIT
cli-cursor,2.1.0,MIT
-cli-spinners,0.1.2,MIT
-cli-table,0.3.1,MIT
-cli-truncate,0.2.1,MIT
cli-width,2.1.0,ISC
clipboard,1.7.1,MIT
cliui,2.1.0,ISC
cliui,4.0.0,ISC
-clone,1.0.2,MIT
clone,1.0.3,MIT
-clone,2.1.1,MIT
-clone-buffer,1.0.0,MIT
clone-response,1.0.2,MIT
-clone-stats,0.0.1,MIT
-clone-stats,1.0.0,MIT
-cloneable-readable,1.0.0,MIT
co,3.0.6,MIT
co,4.6.0,MIT
coa,1.0.1,MIT
@@ -321,7 +314,6 @@ 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.0.3,MIT
colors,1.1.2,MIT
combine-lists,1.0.1,MIT
combined-stream,1.0.6,MIT
@@ -336,7 +328,7 @@ compressible,2.0.11,MIT
compression,1.7.0,MIT
compression-webpack-plugin,1.1.11,MIT
concat-map,0.0.1,MIT
-concat-stream,1.6.0,MIT
+concat-stream,1.6.2,MIT
concurrent-ruby-ext,1.0.5,MIT
configstore,3.1.1,Simplified BSD
connect,3.6.6,MIT
@@ -344,7 +336,7 @@ 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
+consolidate,0.15.1,MIT
constants-browserify,1.0.0,MIT
contains-path,0.1.0,MIT
content-disposition,0.5.2,MIT
@@ -354,11 +346,9 @@ cookie,0.3.1,MIT
cookie-signature,1.0.6,MIT
copy-concurrently,1.0.5,ISC
copy-descriptor,0.1.1,MIT
-copy-webpack-plugin,4.5.1,MIT
core-js,2.3.0,MIT
core-js,2.5.3,MIT
core-util-is,1.0.2,MIT
-cosmiconfig,2.2.2,MIT
crack,0.4.3,MIT
crass,1.0.4,MIT
create-ecdh,4.0.0,MIT
@@ -384,8 +374,6 @@ csso,2.3.2,MIT
currently-unhandled,0.4.1,MIT
custom-event,1.0.1,MIT
cyclist,0.2.2,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
@@ -405,16 +393,12 @@ 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
-dargs,5.1.0,MIT
dashdash,1.14.1,MIT
data-uri-to-buffer,1.2.0,MIT
-date-fns,1.29.0,MIT
date-format,1.2.0,MIT
date-now,0.1.4,MIT
-dateformat,3.0.3,MIT
de-indent,1.0.2,MIT
debug,2.2.0,MIT
debug,2.6.8,MIT
@@ -429,7 +413,6 @@ decode-uri-component,0.2.0,MIT
decompress-response,3.3.0,MIT
deep-equal,1.0.1,MIT
deep-extend,0.4.2,MIT
-deep-extend,0.5.1,MIT
deep-is,0.1.3,MIT
default-require-extensions,1.0.0,MIT
default_value_for,3.0.2,MIT
@@ -448,7 +431,6 @@ depd,1.1.1,MIT
des.js,1.0.0,MIT
descendants_tracker,0.0.4,MIT
destroy,1.0.4,MIT
-detect-conflict,1.0.1,MIT
detect-indent,4.0.0,MIT
detect-libc,1.0.3,Apache 2.0
detect-node,2.0.3,ISC
@@ -456,28 +438,26 @@ device_detector,1.0.0,LGPL
devise,4.4.3,MIT
devise-two-factor,3.0.0,MIT
di,0.0.1,MIT
-diff,3.4.0,New BSD
diff,3.5.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
-dir-glob,2.0.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
+doctrine,2.1.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.20170404,"Simplified BSD,New BSD,Mozilla Public License 2.0"
+domain_name,0.5.20180417,"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.3.2,MIT
-doorkeeper-openid_connect,1.3.0,MIT
+doorkeeper-openid_connect,1.4.0,MIT
dot-prop,4.2.0,MIT
double-ended-queue,2.1.0-0,MIT
dropzone,4.2.0,MIT
@@ -486,12 +466,10 @@ duplexer,0.1.1,MIT
duplexer3,0.1.4,New BSD
duplexify,3.5.3,MIT
ecc-jsbn,0.1.1,MIT
-editions,1.3.4,MIT
+ed25519,1.2.4,MIT
ee-first,1.1.1,MIT
-ejs,2.5.7,Apache 2.0
ejs,2.5.9,Apache 2.0
electron-to-chromium,1.3.3,ISC
-elegant-spinner,1.0.1,MIT
elliptic,6.4.0,MIT
email_reply_trimmer,0.1.6,MIT
emoji-unicode-version,0.2.1,MIT
@@ -506,43 +484,34 @@ enhanced-resolve,0.9.1,MIT
enhanced-resolve,4.0.0,MIT
ent,2.2.0,MIT
entities,1.1.1,Simplified BSD
-envinfo,4.4.2,MIT
equalizer,0.0.11,MIT
errno,0.1.4,MIT
errno,0.1.7,MIT
-error,7.0.2,MIT
error-ex,1.3.0,MIT
-error-ex,1.3.1,MIT
erubis,2.7.0,MIT
es-abstract,1.10.0,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,4.12.1,MIT
+eslint-config-airbnb-base,12.1.0,MIT
+eslint-import-resolver-node,0.3.2,MIT
+eslint-import-resolver-webpack,0.10.0,MIT
+eslint-module-utils,2.2.0,MIT
+eslint-plugin-filenames,1.2.0,MIT
+eslint-plugin-html,4.0.3,ISC
+eslint-plugin-import,2.12.0,MIT
eslint-plugin-jasmine,2.2.0,MIT
-eslint-plugin-promise,3.5.0,ISC
+eslint-plugin-promise,3.8.0,ISC
eslint-plugin-vue,4.0.1,MIT
+eslint-restricted-globals,0.1.1,MIT
eslint-scope,3.7.1,Simplified BSD
eslint-visitor-keys,1.0.0,Apache 2.0
-espree,3.5.2,Simplified BSD
+espree,3.5.4,Simplified BSD
esprima,2.7.3,Simplified BSD
esprima,3.1.3,Simplified BSD
esprima,4.0.0,Simplified BSD
@@ -555,7 +524,6 @@ esutils,2.0.2,Simplified BSD
et-orbi,1.0.3,MIT
etag,1.8.1,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
@@ -564,20 +532,17 @@ evp_bytestokey,1.0.3,MIT
excon,0.62.0,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-brackets,2.1.4,MIT
expand-range,0.1.1,MIT
expand-range,1.8.2,MIT
-expand-tilde,2.0.2,MIT
exports-loader,0.7.0,MIT
express,4.16.2,MIT
expression_parser,0.9.0,MIT
extend,3.0.1,MIT
extend-shallow,2.0.1,MIT
extend-shallow,3.0.2,MIT
-external-editor,2.1.0,MIT
external-editor,2.2.0,MIT
extglob,0.3.2,MIT
extglob,2.0.4,MIT
@@ -587,7 +552,6 @@ faraday,0.12.2,MIT
faraday_middleware,0.12.2,MIT
faraday_middleware-multi_json,0.0.6,MIT
fast-deep-equal,1.0.0,MIT
-fast-glob,2.2.1,MIT
fast-json-stable-stringify,2.0.0,MIT
fast-levenshtein,2.0.6,MIT
fast_blank,1.0.0,MIT
@@ -596,7 +560,6 @@ fastparse,1.1.1,MIT
faye-websocket,0.10.0,MIT
faye-websocket,0.11.1,MIT
ffi,1.9.18,New BSD
-figures,1.7.0,MIT
figures,2.0.0,MIT
file-entry-cache,2.0.0,MIT
file-loader,1.1.11,MIT
@@ -608,16 +571,14 @@ fill-range,2.2.3,MIT
fill-range,4.0.0,MIT
finalhandler,1.1.0,MIT
find-cache-dir,1.0.0,MIT
-find-root,0.1.2,MIT
+find-root,1.1.0,MIT
find-up,1.1.2,MIT
find-up,2.1.0,MIT
-first-chunk-stream,2.0.0,MIT
flat-cache,1.2.2,MIT
flatten,1.0.2,MIT
flipper,0.13.0,MIT
flipper-active_record,0.13.0,MIT
flipper-active_support_cache_store,0.13.0,MIT
-flow-parser,0.66.0,MIT
flowdock,0.7.1,MIT
flush-write-stream,1.0.2,MIT
fog-aliyun,0.2.0,MIT
@@ -653,6 +614,7 @@ fstream,1.0.11,ISC
fstream-ignore,1.0.5,ISC
ftp,0.3.10,MIT
function-bind,1.1.1,MIT
+functional-red-black-tree,1.0.1,MIT
fuzzaldrin-plus,0.5.0,MIT
gauge,2.7.4,ISC
gemnasium-gitlab-service,0.2.6,MIT
@@ -668,11 +630,9 @@ get_process_mem,0.2.0,MIT
getpass,0.1.7,MIT
gettext_i18n_rails,1.8.0,MIT
gettext_i18n_rails_js,1.3.0,MIT
-gh-got,6.0.0,MIT
-gitaly-proto,0.99.0,MIT
+gitaly-proto,0.100.0,MIT
github-linguist,5.3.3,MIT
github-markup,1.7.0,MIT
-github-username,4.1.0,MIT
gitlab-flowdock-git-hook,1.0.1,MIT
gitlab-gollum-lib,4.2.7.2,MIT
gitlab-gollum-rugged_adapter,0.4.4,MIT
@@ -680,41 +640,35 @@ 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,7.1.1,ISC
glob,7.1.2,ISC
-glob-all,3.1.0,MIT
glob-base,0.3.0,MIT
glob-parent,2.0.0,ISC
glob-parent,3.1.0,ISC
-glob-to-regexp,0.3.0,BSD
global-dirs,0.1.1,MIT
-global-modules,1.0.0,MIT
-global-prefix,1.0.2,MIT
+global-modules-path,2.1.0,Apache 2.0
globalid,0.4.1,MIT
-globals,10.4.0,MIT
+globals,11.5.0,MIT
globals,9.18.0,MIT
globby,5.0.0,MIT
globby,6.1.0,MIT
-globby,7.1.1,MIT
-globby,8.0.1,MIT
gollum-grit_adapter,1.0.1,MIT
-gon,6.1.0,MIT
+gon,6.2.0,MIT
good-listener,1.2.2,MIT
google-api-client,0.19.8,Apache 2.0
google-protobuf,3.5.1,New BSD
googleapis-common-protos-types,1.0.1,Apache 2.0
googleauth,0.6.2,Apache 2.0
got,6.7.1,MIT
-got,7.1.0,MIT
got,8.3.0,MIT
gpgme,2.0.13,LGPL-2.1+
graceful-fs,4.1.11,ISC
-grape,1.0.2,MIT
+grape,1.0.3,MIT
grape-entity,0.7.1,MIT
-grape-route-helpers,2.1.0,MIT
+grape-path-helpers,1.0.4,MIT
grape_logging,1.7.0,MIT
+graphiql-rails,1.4.10,MIT
graphlib,2.1.1,MIT
-grouped-queue,0.3.3,MIT
+graphql,1.8.1,MIT
grpc,1.11.0,Apache 2.0
gzip-size,4.1.0,MIT
hamlit,2.6.1,MIT
@@ -728,10 +682,8 @@ har-validator,5.0.3,ISC
has,1.0.1,MIT
has-ansi,2.0.0,MIT
has-binary2,1.0.2,MIT
-has-color,0.1.7,MIT
has-cors,1.1.0,MIT
has-flag,1.0.0,MIT
-has-flag,2.0.0,MIT
has-flag,3.0.0,MIT
has-symbol-support-x,1.3.0,MIT
has-to-string-tag-x,1.3.0,MIT
@@ -756,7 +708,6 @@ hmac-drbg,1.0.1,MIT
hoek,2.16.3,New BSD
hoek,4.2.1,New BSD
home-or-tmp,2.0.0,MIT
-homedir-polyfill,1.0.1,MIT
hosted-git-info,2.2.0,ISC
hpack.js,2.1.6,MIT
html-comment-regex,1.1.1,MIT
@@ -781,17 +732,19 @@ httparty,0.13.7,MIT
httpclient,2.8.3,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.5,MIT
+icalendar,2.4.1,ruby
ice_nine,0.11.2,MIT
iconv-lite,0.4.15,MIT
iconv-lite,0.4.19,MIT
icss-replace-symbols,1.1.0,ISC
icss-utils,2.1.0,ISC
+ieee754,1.1.11,New BSD
ieee754,1.1.8,New BSD
iferr,0.1.5,MIT
-ignore,3.3.7,MIT
+ignore,3.3.8,MIT
ignore-by-default,1.0.1,ISC
immediate,3.0.6,MIT
import-lazy,2.1.0,MIT
@@ -799,7 +752,6 @@ import-local,1.0.0,MIT
imports-loader,0.8.0,MIT
imurmurhash,0.1.4,MIT
indent-string,2.1.0,MIT
-indent-string,3.2.0,MIT
indexes-of,1.0.1,MIT
indexof,0.0.1,MIT*
inflection,1.10.0,MIT
@@ -809,11 +761,9 @@ influxdb,0.2.3,MIT
inherits,2.0.1,ISC
inherits,2.0.3,ISC
ini,1.3.5,ISC
-inquirer,0.12.0,MIT
inquirer,3.3.0,MIT
inquirer,5.2.0,MIT
internal-ip,1.2.0,MIT
-interpret,1.0.1,MIT
interpret,1.1.0,MIT
into-stream,3.1.0,MIT
invariant,2.2.2,New BSD
@@ -822,7 +772,6 @@ ip,1.0.1,MIT
ip,1.1.5,MIT
ipaddr.js,1.6.0,MIT
ipaddress,0.8.3,MIT
-is-absolute,0.2.6,MIT
is-absolute-url,2.1.0,MIT
is-accessor-descriptor,0.1.6,MIT
is-accessor-descriptor,1.0.0,MIT
@@ -836,7 +785,6 @@ is-data-descriptor,1.0.0,MIT
is-date-object,1.0.1,MIT
is-descriptor,0.1.6,MIT
is-descriptor,1.0.2,MIT
-is-directory,0.3.1,MIT
is-dotfile,1.0.3,MIT
is-equal-shallow,0.1.3,MIT
is-extendable,0.1.1,MIT
@@ -859,7 +807,6 @@ is-number,3.0.0,MIT
is-number,4.0.0,MIT
is-obj,1.0.1,MIT
is-object,1.0.1,MIT
-is-observable,0.2.0,MIT
is-odd,2.0.0,MIT
is-path-cwd,1.0.0,MIT
is-path-in-cwd,1.0.0,MIT
@@ -872,17 +819,13 @@ is-promise,2.1.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-scoped,1.0.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
is-windows,1.0.2,MIT
is-wsl,1.1.0,MIT
isarray,0.0.1,MIT
@@ -899,11 +842,9 @@ istanbul-lib-coverage,1.1.1,New BSD
istanbul-lib-coverage,1.2.0,New BSD
istanbul-lib-hook,1.1.0,New BSD
istanbul-lib-instrument,1.10.1,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
-istextorbinary,2.2.1,MIT
isurl,1.0.0,MIT
jasmine-core,2.9.0,MIT
jasmine-jquery,2.1.1,MIT
@@ -918,12 +859,10 @@ js-cookie,2.1.3,MIT
js-tokens,3.0.2,MIT
js-yaml,3.11.0,MIT
js-yaml,3.7.0,MIT
-js-yaml,3.9.1,MIT
jsbn,0.1.1,MIT
-jscodeshift,0.4.1,New BSD
-jscodeshift,0.5.0,New BSD
jsesc,0.5.0,MIT
jsesc,1.3.0,MIT
+jsesc,2.5.1,MIT
json,1.8.6,ruby
json-buffer,3.0.0,MIT
json-jwt,1.9.2,MIT
@@ -931,6 +870,7 @@ json-parse-better-errors,1.0.2,MIT
json-schema,0.2.3,BSD
json-schema-traverse,0.3.1,MIT
json-stable-stringify,1.0.1,MIT
+json-stable-stringify-without-jsonify,1.0.1,MIT
json-stringify-safe,5.0.1,ISC
json3,3.3.2,MIT
json5,0.5.1,MIT
@@ -959,62 +899,44 @@ kind-of,3.2.2,MIT
kind-of,4.0.0,MIT
kind-of,5.1.0,MIT
kind-of,6.0.2,MIT
-kubeclient,3.0.0,MIT
+kubeclient,3.1.0,MIT
latest-version,3.1.0,MIT
lazy-cache,1.0.4,MIT
lazy-cache,2.0.2,MIT
lcid,1.0.0,MIT
+leb,0.3.0,Apache 2.0
levn,0.3.0,MIT
libbase64,0.1.0,MIT
libmime,3.0.0,MIT
libqp,1.1.0,MIT
licensee,8.9.2,MIT
lie,3.1.1,MIT
-listr,0.13.0,MIT
-listr-silent-renderer,1.1.1,MIT
-listr-update-renderer,0.4.0,MIT
-listr-verbose-renderer,0.4.1,MIT
little-plugger,1.1.4,MIT
load-json-file,1.1.0,MIT
-load-json-file,4.0.0,MIT
+load-json-file,2.0.0,MIT
loader-runner,2.3.0,MIT
loader-utils,1.1.0,MIT
locale,2.1.2,"ruby,LGPLv3+"
locate-path,2.0.0,MIT
lodash,4.17.10,MIT
lodash,4.17.4,MIT
-lodash,4.17.5,MIT
-lodash._baseget,3.7.2,MIT
-lodash._topath,3.8.1,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.endswith,4.2.1,MIT
lodash.escaperegexp,4.1.2,MIT
-lodash.get,3.7.0,MIT
-lodash.isarray,3.0.4,MIT
-lodash.isfunction,3.0.9,MIT
-lodash.isstring,4.0.1,MIT
-lodash.kebabcase,4.0.1,MIT
+lodash.kebabcase,4.1.1,MIT
lodash.memoize,4.1.2,MIT
lodash.mergewith,4.6.0,MIT
-lodash.snakecase,4.0.1,MIT
-lodash.startswith,4.2.1,MIT
+lodash.snakecase,4.1.1,MIT
lodash.uniq,4.5.0,MIT
-lodash.words,4.2.0,MIT
-log-symbols,1.0.2,MIT
-log-symbols,2.1.0,MIT
+lodash.upperfirst,4.3.1,MIT
log-symbols,2.2.0,MIT
-log-update,1.0.2,MIT
log4js,2.5.3,Apache 2.0
logging,2.2.2,MIT
loggly,1.1.1,MIT
loglevel,1.4.1,MIT
loglevelnext,1.0.3,MIT
lograge,0.10.0,MIT
+long,3.2.0,Apache 2.0
longest,1.0.1,MIT
loofah,2.2.2,MIT
loose-envify,1.3.1,MIT
@@ -1022,17 +944,17 @@ 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
+lru-cache,4.1.3,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
make-dir,1.2.0,MIT
+mamacro,0.0.3,MIT
map-cache,0.2.2,MIT
map-obj,1.0.1,MIT
-map-stream,0.1.0,UNKNOWN
+map-stream,0.1.0,Unknown
map-visit,1.0.0,MIT
marked,0.3.12,MIT
match-at,0.1.1,MIT
@@ -1040,24 +962,19 @@ math-expression-evaluator,1.2.16,MIT
md5.js,1.3.4,MIT
media-typer,0.3.0,MIT
mem,1.1.0,MIT
-mem-fs,1.1.3,MIT
-mem-fs-editor,4.0.1,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
-merge2,1.2.2,MIT
+merge-source-map,1.1.0,MIT
method_source,0.8.2,MIT
methods,1.1.2,MIT
micromatch,2.3.11,MIT
micromatch,3.1.10,MIT
-micromatch,3.1.6,MIT
-micromatch,3.1.9,MIT
miller-rabin,4.0.1,MIT
mime,1.4.1,MIT
mime,1.6.0,MIT
-mime,2.2.0,MIT
mime,2.3.1,MIT
mime-db,1.33.0,MIT
mime-types,2.1.18,MIT
@@ -1066,6 +983,7 @@ 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_magick,4.8.0,MIT
mini_mime,1.0.0,MIT
mini_portile2,2.3.0,MIT
minimalistic-assert,1.0.0,ISC
@@ -1073,13 +991,13 @@ minimalistic-crypto-utils,1.0.1,MIT
minimatch,3.0.4,ISC
minimist,0.0.10,MIT
minimist,0.0.8,MIT
-minimist,0.1.0,MIT
minimist,1.2.0,MIT
mississippi,2.0.0,Simplified BSD
mixin-deep,1.3.1,MIT
mkdirp,0.5.1,MIT
moment,2.19.2,MIT
-monaco-editor,0.10.0,MIT
+monaco-editor,0.13.1,MIT
+monaco-editor-webpack-plugin,1.2.1,MIT
mousetrap,1.4.6,Apache 2.0
mousetrap-rails,1.4.6,"MIT,Apache"
move-concurrently,1.0.1,ISC
@@ -1089,11 +1007,9 @@ multi_json,1.13.1,MIT
multi_xml,0.6.0,MIT
multicast-dns,6.1.1,MIT
multicast-dns-service-types,1.1.0,MIT
-multimatch,2.1.0,MIT
multipart-post,2.0.0,MIT
mustermann,1.0.2,MIT
mustermann-grape,1.0.0,MIT
-mute-stream,0.0.5,ISC
mute-stream,0.0.7,ISC
mysql2,0.4.10,MIT
nan,2.8.0,MIT
@@ -1102,14 +1018,12 @@ natural-compare,1.4.0,MIT
negotiator,0.6.1,MIT
neo-async,2.5.0,MIT
net-ldap,0.16.0,MIT
-net-ssh,4.2.0,MIT
+net-ssh,5.0.1,MIT
netmask,1.0.6,MIT
netrc,0.11.0,MIT
nice-try,1.0.4,MIT
-node-dir,0.1.8,MIT
node-forge,0.6.33,New BSD
-node-libs-browser,1.1.1,MIT
-node-libs-browser,2.0.0,MIT
+node-libs-browser,2.1.0,MIT
node-pre-gyp,0.6.39,New BSD
node-uuid,1.4.8,MIT
nodemailer,2.7.2,MIT
@@ -1121,7 +1035,6 @@ nodemailer-smtp-transport,2.7.2,MIT
nodemailer-wellknown,0.1.10,MIT
nodemon,1.17.3,MIT
nokogiri,1.8.2,MIT
-nomnom,1.8.1,MIT
nopt,1.0.10,MIT
nopt,3.0.6,ISC
nopt,4.0.1,ISC
@@ -1147,15 +1060,15 @@ object-visit,1.0.1,MIT
object.omit,2.0.1,MIT
object.pick,1.3.0,MIT
obuf,1.1.1,MIT
-octokit,4.8.0,MIT
+octokit,4.9.0,MIT
omniauth,1.8.1,MIT
omniauth-auth0,2.0.0,MIT
-omniauth-authentiq,0.3.1,MIT
+omniauth-authentiq,0.3.3,MIT
omniauth-azure-oauth2,0.0.9,MIT
omniauth-cas3,1.1.4,MIT
omniauth-facebook,4.0.0,MIT
omniauth-github,1.3.0,MIT
-omniauth-gitlab,1.0.2,MIT
+omniauth-gitlab,1.0.3,MIT
omniauth-google-oauth2,0.5.3,MIT
omniauth-kerberos,0.3.0,MIT
omniauth-multipassword,0.4.2,MIT
@@ -1169,46 +1082,36 @@ 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
onetime,2.0.1,MIT
opener,1.4.3,(WTFPL OR MIT)
opn,5.2.0,MIT
optimist,0.6.1,MIT
optionator,0.8.2,MIT
-ora,0.2.3,MIT
org-ruby,0.9.12,MIT
original,1.0.0,MIT
orm_adapter,0.5.0,MIT
os,0.9.6,MIT
-os-browserify,0.2.1,MIT
+os-browserify,0.3.0,MIT
os-homedir,1.0.2,MIT
os-locale,2.1.0,MIT
os-tmpdir,1.0.2,MIT
osenv,0.1.5,ISC
-p-cancelable,0.3.0,MIT
p-cancelable,0.4.1,MIT
-p-each-series,1.0.0,MIT
p-finally,1.0.0,MIT
p-is-promise,1.1.0,MIT
-p-lazy,1.0.0,MIT
p-limit,1.2.0,MIT
p-locate,2.0.0,MIT
p-map,1.1.1,MIT
-p-reduce,1.0.0,MIT
-p-timeout,1.2.1,MIT
p-timeout,2.0.1,MIT
p-try,1.0.0,MIT
pac-proxy-agent,1.1.0,MIT
pac-resolver,2.0.0,MIT
package-json,4.0.1,MIT
-pako,0.2.9,MIT
pako,1.0.6,(MIT AND Zlib)
parallel-transform,1.1.0,MIT
parse-asn1,5.1.0,ISC
parse-glob,3.0.4,MIT
parse-json,2.2.0,MIT
-parse-json,4.0.0,MIT
-parse-passwd,1.0.0,MIT
parseqs,0.0.5,MIT
parseuri,0.0.5,MIT
parseurl,1.3.2,MIT
@@ -1224,7 +1127,7 @@ path-parse,1.0.5,MIT
path-proxy,1.0.0,MIT
path-to-regexp,0.1.7,MIT
path-type,1.1.0,MIT
-path-type,3.0.0,MIT
+path-type,2.0.0,MIT
pause-stream,0.0.11,Apache 2.0
pbkdf2,3.0.14,MIT
peek,1.0.1,MIT
@@ -1244,15 +1147,14 @@ 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
+pluralize,7.0.0,MIT
po_to_json,1.0.1,MIT
+popper.js,1.14.3,MIT
portfinder,1.0.13,MIT
posix-character-classes,0.1.1,MIT
posix-spawn,0.3.13,MIT
postcss,5.2.16,MIT
-postcss,6.0.19,MIT
-postcss,6.0.21,MIT
+postcss,6.0.22,MIT
postcss-calc,5.3.1,MIT
postcss-colormin,2.2.2,MIT
postcss-convert-values,2.6.1,MIT
@@ -1262,9 +1164,6 @@ 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
@@ -1284,6 +1183,7 @@ postcss-reduce-idents,2.4.0,MIT
postcss-reduce-initial,1.0.1,MIT
postcss-reduce-transforms,1.0.4,MIT
postcss-selector-parser,2.2.3,MIT
+postcss-selector-parser,3.1.1,MIT
postcss-svgo,2.1.6,MIT
postcss-unique-selectors,2.0.2,MIT
postcss-value-parser,3.3.0,MIT
@@ -1294,17 +1194,15 @@ premailer-rails,1.9.7,MIT
prepend-http,1.0.4,MIT
prepend-http,2.0.0,MIT
preserve,0.2.0,MIT
-prettier,1.10.2,MIT
prettier,1.11.1,MIT
-prettier,1.8.2,MIT
-pretty-bytes,4.0.2,MIT
+prettier,1.12.1,MIT
prismjs,1.6.0,MIT
private,0.1.8,MIT
process,0.11.10,MIT
process-nextick-args,1.0.7,MIT
process-nextick-args,2.0.0,MIT
-progress,1.1.8,MIT
-prometheus-client-mmap,0.9.1,Apache 2.0
+progress,2.0.0,MIT
+prometheus-client-mmap,0.9.3,Apache 2.0
promise-inflight,1.0.1,ISC
proxy-addr,2.0.3,MIT
proxy-agent,2.0.0,MIT
@@ -1359,26 +1257,20 @@ 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.5,(BSD-2-Clause OR MIT OR Apache-2.0)
-rdoc,4.2.2,ruby
+rdoc,6.0.4,ruby
re2,1.1.1,New BSD
-read-chunk,2.1.0,MIT
read-pkg,1.1.0,MIT
-read-pkg,3.0.0,MIT
+read-pkg,2.0.0,MIT
read-pkg-up,1.0.1,MIT
-read-pkg-up,3.0.0,MIT
+read-pkg-up,2.0.0,MIT
readable-stream,1.1.14,MIT
readable-stream,2.0.6,MIT
readable-stream,2.3.4,MIT
+readable-stream,2.3.6,MIT
readdirp,2.1.0,MIT
-readline2,1.0.1,MIT
recaptcha,3.0.0,MIT
-recast,0.12.9,MIT
-recast,0.14.7,MIT
-rechoir,0.6.2,MIT
-recursive-open-struct,1.0.5,MIT
+recursive-open-struct,1.1.0,MIT
redcarpet,3.4.0,MIT
redent,1.0.0,MIT
redis,2.8.0,MIT
@@ -1386,7 +1278,7 @@ 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-namespace,1.6.0,MIT
redis-parser,2.6.0,MIT
redis-rack,2.0.4,MIT
redis-rails,5.0.2,MIT
@@ -1409,8 +1301,6 @@ repeat-element,1.1.2,MIT
repeat-string,0.2.2,MIT
repeat-string,1.6.1,MIT
repeating,2.0.1,MIT
-replace-ext,0.0.1,MIT
-replace-ext,1.0.0,MIT
representable,3.0.4,MIT
request,2.75.0,Apache 2.0
request,2.81.0,Apache 2.0
@@ -1419,28 +1309,22 @@ request_store,1.3.1,MIT
requestretry,1.13.0,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.5.0,MIT
resolve,1.7.1,MIT
resolve-cwd,2.0.0,MIT
-resolve-dir,1.0.1,MIT
resolve-from,1.0.1,MIT
resolve-from,3.0.0,MIT
resolve-url,0.2.1,MIT
responders,2.4.0,MIT
responselike,1.0.2,MIT
rest-client,2.0.2,MIT
-restore-cursor,1.0.1,MIT
restore-cursor,2.0.0,MIT
ret,0.1.15,MIT
retriable,3.1.1,MIT
right-align,0.1.3,MIT
-rimraf,2.2.8,MIT
-rimraf,2.6.1,ISC
rimraf,2.6.2,ISC
rinku,2.0.0,ISC
ripemd160,2.0.1,MIT
@@ -1451,16 +1335,15 @@ rqrcode-rails3,0.1.7,MIT
ruby-enum,0.7.2,MIT
ruby-fogbugz,0.2.1,MIT
ruby-prof,0.17.0,Simplified BSD
+ruby-progressbar,1.9.0,MIT
ruby-saml,1.7.2,MIT
ruby_parser,3.9.0,MIT
rubyntlm,0.6.2,MIT
rubypants,0.2.0,BSD
rufus-scheduler,3.4.0,MIT
-rugged,0.27.0,MIT
-run-async,0.1.0,MIT
+rugged,0.27.1,MIT
run-async,2.3.0,MIT
run-queue,1.0.3,ISC
-rx-lite,3.1.2,Apache 2.0
rx-lite,4.0.8,Apache 2.0
rx-lite-aggregates,4.0.8,Apache 2.0
rxjs,5.5.10,Apache 2.0
@@ -1475,7 +1358,6 @@ sass-rails,5.0.6,MIT
sawyer,0.8.1,MIT
sax,1.2.2,ISC
schema-utils,0.4.5,MIT
-scoped-regex,1.0.0,MIT
securecompare,1.0.0,MIT
seed-fu,2.3.7,MIT
select,1.1.2,MIT
@@ -1484,7 +1366,6 @@ 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,5.5.0,ISC
semver-diff,2.1.0,MIT
send,0.16.1,MIT
@@ -1506,9 +1387,7 @@ sha.js,2.4.10,MIT
sha1,1.1.1,New BSD
shebang-command,1.2.0,MIT
shebang-regex,1.0.0,MIT
-shelljs,0.7.8,New BSD
-shelljs,0.8.1,New BSD
-sidekiq,5.0.5,LGPL
+sidekiq,5.1.3,LGPL
sidekiq-cron,0.6.0,MIT
sidekiq-limit_fetch,3.4.0,MIT
signal-exit,3.0.2,ISC
@@ -1516,8 +1395,7 @@ signet,0.8.1,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
+slice-ansi,1.0.0,MIT
smart-buffer,1.1.15,MIT
smtp-connection,2.12.0,MIT
snapdragon,0.8.1,MIT
@@ -1536,6 +1414,7 @@ socks,1.1.9,MIT
socks-proxy-agent,2.1.1,MIT
sort-keys,1.1.2,MIT
sort-keys,2.0.0,MIT
+sortablejs,1.7.0,MIT
source-list-map,2.0.0,MIT
source-map,0.2.0,New BSD
source-map,0.4.4,New BSD
@@ -1567,53 +1446,46 @@ state_machines-activerecord,0.5.1,MIT
static-extend,0.1.2,MIT
statuses,1.3.1,MIT
statuses,1.4.0,MIT
+stickyfilljs,2.0.5,MIT
stream-browserify,2.0.1,MIT
stream-combiner,0.0.4,MIT
stream-each,1.2.2,MIT
-stream-http,2.8.0,MIT
+stream-http,2.8.2,MIT
stream-shift,1.0.0,MIT
-stream-to-observable,0.2.0,MIT
streamroller,0.7.0,MIT
strict-uri-encode,1.1.0,MIT
-string-template,0.2.1,MIT
string-width,1.0.2,MIT
string-width,2.1.1,MIT
string_decoder,0.10.31,MIT
string_decoder,1.0.3,MIT
+string_decoder,1.1.1,MIT
stringex,2.8.4,MIT
stringstream,0.0.5,MIT
-strip-ansi,0.1.1,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-bom-stream,2.0.0,MIT
strip-eof,1.0.0,MIT
strip-indent,1.0.1,MIT
strip-json-comments,2.0.1,MIT
style-loader,0.21.0,MIT
supports-color,2.0.0,MIT
supports-color,3.2.3,MIT
-supports-color,5.1.0,MIT
-supports-color,5.2.0,MIT
supports-color,5.4.0,MIT
svg4everybody,2.1.9,CC0-1.0
svgo,0.7.2,MIT
-symbol-observable,0.2.4,MIT
symbol-observable,1.0.1,MIT
sys-filesystem,1.1.6,Artistic 2.0
-table,3.8.3,New BSD
+table,4.0.2,New BSD
tapable,0.1.10,MIT
tapable,1.0.0,MIT
tar,2.2.1,ISC
tar-pack,3.4.1,Simplified BSD
-temp,0.8.3,MIT
temple,0.7.7,MIT
term-size,1.2.0,MIT
test-exclude,4.2.1,ISC
text,1.3.1,MIT
text-table,0.2.0,MIT
-textextensions,2.2.0,MIT
thor,0.19.4,MIT
thread_safe,0.3.6,Apache 2.0
three,0.84.0,MIT
@@ -1626,8 +1498,7 @@ thunky,0.1.0,MIT*
tilt,2.0.6,MIT
timeago.js,3.0.2,MIT
timed-out,4.0.1,MIT
-timers-browserify,1.4.2,MIT
-timers-browserify,2.0.4,MIT
+timers-browserify,2.0.10,MIT
timespan,2.3.0,MIT
timfel-krb5-auth,0.8.3,LGPL
tiny-emitter,2.0.2,MIT
@@ -1637,7 +1508,6 @@ to-arraybuffer,1.0.1,MIT
to-fast-properties,1.0.3,MIT
to-fast-properties,2.0.0,MIT
to-object-path,0.3.0,MIT
-to-regex,3.0.1,MIT
to-regex,3.0.2,MIT
to-regex-range,2.1.1,MIT
toml-rb,1.0.0,MIT
@@ -1667,9 +1537,7 @@ uglify-to-browserify,1.0.2,MIT
uglifyjs-webpack-plugin,1.2.5,MIT
uid-number,0.0.6,ISC
ultron,1.1.1,MIT
-unc-path-regex,0.1.2,MIT
undefsafe,2.0.2,MIT
-underscore,1.6.0,MIT
underscore,1.7.0,MIT
underscore,1.9.0,MIT
unf,0.1.4,BSD
@@ -1685,9 +1553,8 @@ unique-slug,2.0.0,ISC
unique-string,1.0.0,MIT
unpipe,1.0.0,MIT
unset-value,1.0.0,MIT
-untildify,3.0.2,MIT
unzip-response,2.0.1,MIT
-upath,1.0.2,MIT
+upath,1.0.5,MIT
update-notifier,2.3.0,Simplified BSD
urix,0.1.0,MIT
url,0.11.0,MIT
@@ -1701,14 +1568,13 @@ url-parse-lax,3.0.0,MIT
url-to-options,1.0.1,MIT
url_safe_base64,0.2.2,MIT
use,2.0.2,MIT
-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.1,MIT
uuid,3.2.1,MIT
uws,9.14.0,Zlib
-v8-compile-cache,1.1.2,MIT
+v8-compile-cache,2.0.0,MIT
validate-npm-package-license,3.0.1,Apache 2.0
validates_hostname,1.0.6,MIT
vary,1.1.1,MIT
@@ -1716,18 +1582,15 @@ vary,1.1.2,MIT
vendors,1.0.1,MIT
verror,1.10.0,MIT
version_sorter,2.1.0,MIT
-vinyl,1.2.0,MIT
-vinyl,2.1.0,MIT
-vinyl-file,2.0.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.16,MIT
-vue-eslint-parser,2.0.1,MIT
+vue-eslint-parser,2.0.3,MIT
vue-hot-reload-api,2.3.0,MIT
-vue-loader,14.2.2,MIT
+vue-loader,15.2.0,MIT
vue-resource,1.5.0,MIT
vue-router,3.0.1,MIT
vue-style-loader,4.1.0,MIT
@@ -1738,17 +1601,14 @@ vuex,3.0.1,MIT
warden,1.2.7,MIT
watchpack,1.5.0,MIT
wbuf,1.7.2,MIT
-webpack,4.7.0,MIT
-webpack-addons,1.1.5,MIT
+webpack,4.11.1,MIT
webpack-bundle-analyzer,2.11.1,MIT
-webpack-cli,2.1.2,MIT
+webpack-cli,3.0.2,MIT
webpack-dev-middleware,2.0.6,MIT
webpack-dev-middleware,3.1.3,MIT
webpack-dev-server,3.1.4,MIT
-webpack-log,1.1.2,MIT
webpack-log,1.2.0,MIT
webpack-rails,0.9.10,MIT
-webpack-sources,1.0.1,MIT
webpack-sources,1.1.0,MIT
webpack-stats-plugin,0.2.1,MIT
websocket-driver,0.6.5,MIT
@@ -1765,11 +1625,10 @@ wordwrap,0.0.2,MIT
wordwrap,0.0.3,MIT
wordwrap,1.0.0,MIT
worker-farm,1.5.2,MIT
-worker-loader,1.1.1,MIT
+worker-loader,2.0.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
write-file-atomic,2.3.0,ISC
ws,3.3.3,MIT
ws,4.0.0,MIT
@@ -1781,12 +1640,8 @@ xtend,4.0.1,MIT
y18n,3.2.1,ISC
y18n,4.0.0,ISC
yallist,2.1.2,ISC
-yargs,1.2.6,MIT
yargs,11.0.0,MIT
yargs,11.1.0,MIT
yargs,3.10.0,MIT
yargs-parser,9.0.2,ISC
yeast,0.1.2,MIT
-yeoman-environment,2.0.5,Simplified BSD
-yeoman-environment,2.0.6,Simplified BSD
-yeoman-generator,2.0.5,Simplified BSD
diff --git a/yarn.lock b/yarn.lock
index 418fa4a2216..65d78173a5b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -82,13 +82,6 @@
version "1.23.0"
resolved "https://registry.yarnpkg.com/@gitlab-org/gitlab-svgs/-/gitlab-svgs-1.23.0.tgz#42047aeedcc06bc12d417ed1efadad1749af9670"
-"@mrmlnc/readdir-enhanced@^2.2.1":
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
- dependencies:
- call-me-maybe "^1.0.1"
- glob-to-regexp "^0.3.0"
-
"@sindresorhus/is@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
@@ -111,6 +104,141 @@
source-map "^0.5.6"
vue-template-es2015-compiler "^1.6.0"
+"@webassemblyjs/ast@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.10.tgz#7f1e81149ca4e103c9e7cc321ea0dcb83a392512"
+ dependencies:
+ "@webassemblyjs/helper-module-context" "1.5.10"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
+ "@webassemblyjs/wast-parser" "1.5.10"
+ debug "^3.1.0"
+ mamacro "^0.0.3"
+
+"@webassemblyjs/floating-point-hex-parser@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.10.tgz#ae48705fd58927df62023f114520b8215330ff86"
+
+"@webassemblyjs/helper-api-error@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.10.tgz#0baf9453ce2fd8db58f0fdb4fb2852557c71d5a7"
+
+"@webassemblyjs/helper-buffer@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.10.tgz#abee4284161e9cd6ba7619785ca277bfcb8052ce"
+ dependencies:
+ debug "^3.1.0"
+
+"@webassemblyjs/helper-code-frame@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.10.tgz#4e23c05431665f16322104580af7c06253d4b4e0"
+ dependencies:
+ "@webassemblyjs/wast-printer" "1.5.10"
+
+"@webassemblyjs/helper-fsm@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.10.tgz#490bab613ea255a9272b764826d3cc9d15170676"
+
+"@webassemblyjs/helper-module-context@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.10.tgz#6fca93585228bf33e6da076d0a1373db1fdd6580"
+ dependencies:
+ mamacro "^0.0.3"
+
+"@webassemblyjs/helper-wasm-bytecode@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.10.tgz#90f6da93c7a186bfb2f587de442982ff533c4b44"
+
+"@webassemblyjs/helper-wasm-section@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.10.tgz#d64292a19f7f357c49719461065efdf7ec975d66"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/helper-buffer" "1.5.10"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
+ "@webassemblyjs/wasm-gen" "1.5.10"
+ debug "^3.1.0"
+
+"@webassemblyjs/ieee754@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.10.tgz#257cad440dd6c8a339402d31e035ba2e38e9c245"
+ dependencies:
+ ieee754 "^1.1.11"
+
+"@webassemblyjs/leb128@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.10.tgz#a8e4fe5f4b16daadb241fcc44d9735e9f27b05a3"
+ dependencies:
+ leb "^0.3.0"
+
+"@webassemblyjs/utf8@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.10.tgz#0b3b6bc86b7619c5dc7b2789db6665aa35689983"
+
+"@webassemblyjs/wasm-edit@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.10.tgz#0fe80f19e57f669eab1caa8c1faf9690b259d5b9"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/helper-buffer" "1.5.10"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
+ "@webassemblyjs/helper-wasm-section" "1.5.10"
+ "@webassemblyjs/wasm-gen" "1.5.10"
+ "@webassemblyjs/wasm-opt" "1.5.10"
+ "@webassemblyjs/wasm-parser" "1.5.10"
+ "@webassemblyjs/wast-printer" "1.5.10"
+ debug "^3.1.0"
+
+"@webassemblyjs/wasm-gen@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.10.tgz#8b29ddd3651259408ae5d5c816a011fb3f3f3584"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
+ "@webassemblyjs/ieee754" "1.5.10"
+ "@webassemblyjs/leb128" "1.5.10"
+ "@webassemblyjs/utf8" "1.5.10"
+
+"@webassemblyjs/wasm-opt@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.10.tgz#569e45ab1b2bf0a7706cdf6d1b51d1188e9e4c7b"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/helper-buffer" "1.5.10"
+ "@webassemblyjs/wasm-gen" "1.5.10"
+ "@webassemblyjs/wasm-parser" "1.5.10"
+ debug "^3.1.0"
+
+"@webassemblyjs/wasm-parser@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.10.tgz#3e1017e49f833f46b840db7cf9d194d4f00037ff"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/helper-api-error" "1.5.10"
+ "@webassemblyjs/helper-wasm-bytecode" "1.5.10"
+ "@webassemblyjs/ieee754" "1.5.10"
+ "@webassemblyjs/leb128" "1.5.10"
+ "@webassemblyjs/wasm-parser" "1.5.10"
+
+"@webassemblyjs/wast-parser@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.10.tgz#1a3235926483c985a00ee8ebca856ffda9544934"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/floating-point-hex-parser" "1.5.10"
+ "@webassemblyjs/helper-api-error" "1.5.10"
+ "@webassemblyjs/helper-code-frame" "1.5.10"
+ "@webassemblyjs/helper-fsm" "1.5.10"
+ long "^3.2.0"
+ mamacro "^0.0.3"
+
+"@webassemblyjs/wast-printer@1.5.10":
+ version "1.5.10"
+ resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.10.tgz#adb38831ba45efd0a5c7971b666e179b64f68bba"
+ dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/wast-parser" "1.5.10"
+ long "^3.2.0"
+
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -142,10 +270,14 @@ acorn@^3.0.4:
version "3.3.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a"
-acorn@^5.0.0, acorn@^5.3.0, acorn@^5.5.0:
+acorn@^5.0.0, acorn@^5.3.0:
version "5.5.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
+acorn@^5.5.0:
+ version "5.6.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.2.tgz#b1da1d7be2ac1b4a327fb9eab851702c5045b4e7"
+
addressparser@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746"
@@ -225,10 +357,6 @@ ansi-align@^2.0.0:
dependencies:
string-width "^2.0.0"
-ansi-escapes@^1.0.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-1.4.0.tgz#d3a8a83b319aa67793662b13e761c7911422306e"
-
ansi-escapes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92"
@@ -255,14 +383,6 @@ ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@~1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.0.0.tgz#cb102df1c56f5123eab8b67cd7b98027a0279178"
-
-any-observable@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/any-observable/-/any-observable-0.2.0.tgz#c67870058003579009083f54ac0abafb5c33d242"
-
anymatch@^1.3.0:
version "1.3.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a"
@@ -318,10 +438,6 @@ arr-union@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4"
-array-differ@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-1.0.0.tgz#eff52e3758249d33be402b8bb8e564bb2b5d4031"
-
array-find-index@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1"
@@ -405,11 +521,7 @@ 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.10.1:
- version "0.10.1"
- resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.10.1.tgz#f52fca9715579a14f841d67d7f8d25432ab6a3dd"
-
-ast-types@0.11.3, ast-types@0.x.x:
+ast-types@0.x.x:
version "0.11.3"
resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.3.tgz#c20757fe72ee71278ea0ff3d87e5c2ca30d9edf8"
@@ -421,11 +533,11 @@ async-limiter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
-async@1.x, async@^1.4.0, async@^1.5.0, async@^1.5.2:
+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.0.0, async@^2.1.4, async@^2.6.0:
+async@^2.0.0, async@^2.1.4:
version "2.6.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
dependencies:
@@ -706,10 +818,6 @@ babel-plugin-syntax-async-generators@^6.5.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a"
-babel-plugin-syntax-class-constructor-call@^6.18.0:
- version "6.18.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416"
-
babel-plugin-syntax-class-properties@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de"
@@ -726,14 +834,6 @@ babel-plugin-syntax-exponentiation-operator@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de"
-babel-plugin-syntax-export-extensions@^6.8.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721"
-
-babel-plugin-syntax-flow@^6.18.0:
- version "6.18.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d"
-
babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-spread@^6.8.0:
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
@@ -758,14 +858,6 @@ babel-plugin-transform-async-to-generator@^6.24.1:
babel-plugin-syntax-async-functions "^6.8.0"
babel-runtime "^6.22.0"
-babel-plugin-transform-class-constructor-call@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9"
- dependencies:
- babel-plugin-syntax-class-constructor-call "^6.18.0"
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
babel-plugin-transform-class-properties@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac"
@@ -968,20 +1060,6 @@ babel-plugin-transform-exponentiation-operator@^6.24.1:
babel-plugin-syntax-exponentiation-operator "^6.8.0"
babel-runtime "^6.22.0"
-babel-plugin-transform-export-extensions@^6.22.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653"
- dependencies:
- babel-plugin-syntax-export-extensions "^6.8.0"
- babel-runtime "^6.22.0"
-
-babel-plugin-transform-flow-strip-types@^6.8.0:
- version "6.22.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf"
- dependencies:
- babel-plugin-syntax-flow "^6.18.0"
- babel-runtime "^6.22.0"
-
babel-plugin-transform-object-rest-spread@^6.22.0:
version "6.23.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.23.0.tgz#875d6bc9be761c58a2ae3feee5dc4895d8c7f921"
@@ -1002,7 +1080,7 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
-babel-preset-es2015@^6.24.1, babel-preset-es2015@^6.9.0:
+babel-preset-es2015@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz#d44050d6bc2c9feea702aaf38d727a0210538939"
dependencies:
@@ -1052,14 +1130,6 @@ babel-preset-latest@^6.24.1:
babel-preset-es2016 "^6.24.1"
babel-preset-es2017 "^6.24.1"
-babel-preset-stage-1@^6.5.0:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0"
- dependencies:
- babel-plugin-transform-class-constructor-call "^6.24.1"
- babel-plugin-transform-export-extensions "^6.22.0"
- babel-preset-stage-2 "^6.24.1"
-
babel-preset-stage-2@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1"
@@ -1079,7 +1149,7 @@ babel-preset-stage-3@^6.24.1:
babel-plugin-transform-exponentiation-operator "^6.24.1"
babel-plugin-transform-object-rest-spread "^6.22.0"
-babel-register@^6.26.0, babel-register@^6.9.0:
+babel-register@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
dependencies:
@@ -1131,11 +1201,11 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26
lodash "^4.17.4"
to-fast-properties "^1.0.3"
-babylon@7.0.0-beta.44, babylon@^7.0.0-beta.30:
+babylon@7.0.0-beta.44:
version "7.0.0-beta.44"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.44.tgz#89159e15e6e30c5096e22d738d8c0af8a0e8ca1d"
-babylon@^6.17.3, babylon@^6.18.0:
+babylon@^6.18.0:
version "6.18.0"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
@@ -1207,10 +1277,6 @@ binary-extensions@^1.0.0:
version "1.11.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205"
-binaryextensions@2:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.1.1.tgz#3209a51ca4a4ad541a3b8d3d6a6d5b83a2485935"
-
bitsyntax@~0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/bitsyntax/-/bitsyntax-0.0.4.tgz#eb10cc6f82b8c490e3e85698f07e83d46e0cba82"
@@ -1518,10 +1584,6 @@ cacheable-request@^2.1.1:
normalize-url "2.0.1"
responselike "1.0.2"
-call-me-maybe@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
-
caller-path@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f"
@@ -1587,7 +1649,7 @@ center-align@^0.1.1:
align-text "^0.1.3"
lazy-cache "^1.0.3"
-chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
+chalk@^1.1.1, chalk@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
dependencies:
@@ -1597,7 +1659,7 @@ chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
strip-ansi "^3.0.0"
supports-color "^2.0.0"
-chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1:
+chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
dependencies:
@@ -1605,14 +1667,6 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@~0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/chalk/-/chalk-0.4.0.tgz#5199a3ddcd0c1efe23bc08c1b027b06176e0c64f"
- dependencies:
- ansi-styles "~1.0.0"
- has-color "~0.1.0"
- strip-ansi "~0.1.0"
-
chardet@^0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2"
@@ -1708,35 +1762,12 @@ 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.2:
- 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-spinners@^0.1.2:
- version "0.1.2"
- resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-0.1.2.tgz#bb764d88e185fb9e1e6a2a1f19772318f605e31c"
-
-cli-table@^0.3.1:
- version "0.3.1"
- resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.1.tgz#f53b05266a8b1a0b934b3d0821e6e2dc5914ae23"
- dependencies:
- colors "1.0.3"
-
-cli-truncate@^0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
- dependencies:
- slice-ansi "0.0.4"
- string-width "^1.0.1"
-
cli-width@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
@@ -1765,40 +1796,16 @@ cliui@^4.0.0:
strip-ansi "^4.0.0"
wrap-ansi "^2.0.0"
-clone-buffer@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
-
clone-response@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
dependencies:
mimic-response "^1.0.0"
-clone-stats@^0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1"
-
-clone-stats@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680"
-
-clone@^1.0.0, clone@^1.0.2:
+clone@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f"
-clone@^2.1.1:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.1.tgz#d217d1e961118e3ac9a4b8bba3285553bf647cdb"
-
-cloneable-readable@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117"
- dependencies:
- inherits "^2.0.1"
- process-nextick-args "^1.0.6"
- through2 "^2.0.1"
-
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -1856,11 +1863,7 @@ colormin@^1.0.5:
css-color-names "0.0.4"
has "^1.0.1"
-colors@1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
-
-colors@^1.1.0, colors@^1.1.2, colors@~1.1.2:
+colors@^1.1.0, colors@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
@@ -2024,19 +2027,6 @@ 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.5.1:
- version "4.5.1"
- resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.5.1.tgz#fc4f68f4add837cc5e13d111b20715793225d29c"
- dependencies:
- cacache "^10.0.4"
- find-cache-dir "^1.0.0"
- globby "^7.1.1"
- is-glob "^4.0.0"
- loader-utils "^1.1.0"
- minimatch "^3.0.4"
- p-limit "^1.0.0"
- serialize-javascript "^1.4.0"
-
core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0:
version "2.5.3"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e"
@@ -2357,10 +2347,6 @@ dagre-layout@^0.8.0:
graphlib "^2.1.1"
lodash "^4.17.4"
-dargs@^5.1.0:
- version "5.1.0"
- resolved "https://registry.yarnpkg.com/dargs/-/dargs-5.1.0.tgz#ec7ea50c78564cd36c9d5ec18f66329fade27829"
-
dashdash@^1.12.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0"
@@ -2371,10 +2357,6 @@ data-uri-to-buffer@1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-1.2.0.tgz#77163ea9c20d8641b4707e8f18abdf9a78f34835"
-date-fns@^1.27.2:
- version "1.29.0"
- resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.29.0.tgz#12e609cdcb935127311d04d33334e2960a2a54e6"
-
date-format@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/date-format/-/date-format-1.2.0.tgz#615e828e233dd1ab9bb9ae0950e0ceccfa6ecad8"
@@ -2383,10 +2365,6 @@ date-now@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b"
-dateformat@^3.0.3:
- version "3.0.3"
- resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
-
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
@@ -2427,7 +2405,7 @@ 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, decompress-response@^3.3.0:
+decompress-response@^3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
dependencies:
@@ -2437,10 +2415,6 @@ deep-equal@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5"
-deep-extend@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
-
deep-extend@~0.4.0:
version "0.4.2"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f"
@@ -2543,10 +2517,6 @@ destroy@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
-detect-conflict@^1.0.0:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/detect-conflict/-/detect-conflict-1.0.1.tgz#088657a66a961c05019db7c4230883b1c6b4176e"
-
detect-indent@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
@@ -2565,7 +2535,7 @@ di@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
-diff@^3.3.1, diff@^3.4.0, diff@^3.5.0:
+diff@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -2577,13 +2547,6 @@ 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"
@@ -2696,15 +2659,11 @@ ecc-jsbn@~0.1.1:
dependencies:
jsbn "~0.1.0"
-editions@^1.3.3:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b"
-
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
-ejs@^2.5.7, ejs@^2.5.9:
+ejs@^2.5.7:
version "2.5.9"
resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.9.tgz#7ba254582a560d267437109a68354112475b0ce5"
@@ -2712,10 +2671,6 @@ 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"
-elegant-spinner@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
-
elliptic@^6.0.0:
version "6.4.0"
resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df"
@@ -2809,10 +2764,6 @@ entities@^1.1.1, entities@~1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
-envinfo@^4.4.2:
- version "4.4.2"
- resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-4.4.2.tgz#472c49f3a8b9bca73962641ce7cb692bf623cd1c"
-
errno@^0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.4.tgz#b896e23a9e5e8ba33871fc996abd3635fc9a1c7d"
@@ -2831,19 +2782,6 @@ error-ex@^1.2.0:
dependencies:
is-arrayish "^0.2.1"
-error-ex@^1.3.1:
- version "1.3.1"
- resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc"
- dependencies:
- is-arrayish "^0.2.1"
-
-error@^7.0.2:
- version "7.0.2"
- resolved "https://registry.yarnpkg.com/error/-/error-7.0.2.tgz#a5f75fff4d9926126ddac0ea5dc38e689153cb02"
- dependencies:
- string-template "~0.2.1"
- xtend "~4.0.0"
-
es-abstract@^1.7.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864"
@@ -2969,12 +2907,11 @@ eslint-plugin-promise@^3.8.0:
version "3.8.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621"
-eslint-plugin-vue@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.0.1.tgz#afda92cfd7e7363b1fbdb1a772dd63359a9ce96a"
+eslint-plugin-vue@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-4.5.0.tgz#09d6597f4849e31a3846c2c395fccf17685b69c3"
dependencies:
- require-all "^2.2.0"
- vue-eslint-parser "^2.0.1"
+ vue-eslint-parser "^2.0.3"
eslint-restricted-globals@^0.1.1:
version "0.1.1"
@@ -3048,35 +2985,30 @@ esprima@3.x.x, esprima@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
-esprima@^4.0.0, esprima@~4.0.0:
+esprima@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804"
esquery@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa"
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708"
dependencies:
estraverse "^4.0.0"
esrecurse@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.1.0.tgz#4713b6536adf7f2ac4f327d559e7756bff648220"
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf"
dependencies:
- estraverse "~4.1.0"
- object-assign "^4.0.1"
+ estraverse "^4.1.0"
estraverse@^1.9.1:
version "1.9.3"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44"
-estraverse@^4.0.0, estraverse@^4.1.1, estraverse@^4.2.0:
+estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13"
-estraverse@~4.1.0:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.1.1.tgz#f6caca728933a850ef90661d0e17982ba47111a2"
-
esutils@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b"
@@ -3134,10 +3066,6 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
-exit-hook@^1.0.0:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8"
-
expand-braces@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/expand-braces/-/expand-braces-0.1.2.tgz#488b1d1d2451cb3d3a6b192cfc030f44c5855fea"
@@ -3177,12 +3105,6 @@ expand-range@^1.8.1:
dependencies:
fill-range "^2.1.0"
-expand-tilde@^2.0.0, expand-tilde@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
- dependencies:
- homedir-polyfill "^1.0.1"
-
exports-loader@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.7.0.tgz#84881c784dea6036b8e1cd1dac3da9b6409e21a5"
@@ -3281,16 +3203,6 @@ 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"
-fast-glob@^2.0.2:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.1.tgz#686c2345be88f3741e174add0be6f2e5b6078889"
- dependencies:
- "@mrmlnc/readdir-enhanced" "^2.2.1"
- glob-parent "^3.1.0"
- is-glob "^4.0.0"
- merge2 "^1.2.1"
- micromatch "^3.1.10"
-
fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
@@ -3315,13 +3227,6 @@ faye-websocket@~0.11.0:
dependencies:
websocket-driver ">=0.5.1"
-figures@^1.7.0:
- version "1.7.0"
- resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
- dependencies:
- 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"
@@ -3417,12 +3322,6 @@ find-up@^2.0.0, find-up@^2.1.0:
dependencies:
locate-path "^2.0.0"
-first-chunk-stream@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70"
- dependencies:
- readable-stream "^2.0.2"
-
flat-cache@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.2.2.tgz#fa86714e72c21db88601761ecf2f555d1abc6b96"
@@ -3436,10 +3335,6 @@ flatten@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
-flow-parser@^0.*:
- version "0.66.0"
- resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.66.0.tgz#be583fefb01192aa5164415d31a6241b35718983"
-
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"
@@ -3644,26 +3539,6 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
-gh-got@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/gh-got/-/gh-got-6.0.0.tgz#d74353004c6ec466647520a10bd46f7299d268d0"
- dependencies:
- got "^7.0.0"
- is-plain-obj "^1.1.0"
-
-github-username@^4.0.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/github-username/-/github-username-4.1.0.tgz#cbe280041883206da4212ae9e4b5f169c30bf417"
- dependencies:
- gh-got "^6.0.0"
-
-glob-all@^3.1.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/glob-all/-/glob-all-3.1.0.tgz#8913ddfb5ee1ac7812656241b03d5217c64b02ab"
- dependencies:
- glob "^7.0.5"
- yargs "~1.2.6"
-
glob-base@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
@@ -3684,10 +3559,6 @@ glob-parent@^3.1.0:
is-glob "^3.1.0"
path-dirname "^1.0.0"
-glob-to-regexp@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
-
glob@^5.0.15:
version "5.0.15"
resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1"
@@ -3698,7 +3569,7 @@ glob@^5.0.15:
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.1.2:
+glob@^7.0.3, glob@^7.0.5, 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:
@@ -3715,23 +3586,9 @@ global-dirs@^0.1.0:
dependencies:
ini "^1.3.4"
-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"
+global-modules-path@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.1.0.tgz#923ec524e8726bb0c1a4ed4b8e21e1ff80c88bbb"
globals@^11.0.1, globals@^11.1.0:
version "11.5.0"
@@ -3762,29 +3619,6 @@ 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"
-
-globby@^8.0.0, globby@^8.0.1:
- version "8.0.1"
- resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50"
- dependencies:
- array-union "^1.0.1"
- dir-glob "^2.0.0"
- fast-glob "^2.0.2"
- 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"
@@ -3807,26 +3641,7 @@ got@^6.7.1:
unzip-response "^2.0.1"
url-parse-lax "^1.0.0"
-got@^7.0.0:
- version "7.1.0"
- resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
- dependencies:
- decompress-response "^3.2.0"
- duplexer3 "^0.1.4"
- get-stream "^3.0.0"
- is-plain-obj "^1.1.0"
- is-retry-allowed "^1.0.0"
- is-stream "^1.0.0"
- isurl "^1.0.0-alpha5"
- lowercase-keys "^1.0.0"
- p-cancelable "^0.3.0"
- p-timeout "^1.1.1"
- safe-buffer "^5.0.1"
- timed-out "^4.0.0"
- url-parse-lax "^1.0.0"
- url-to-options "^1.0.1"
-
-got@^8.0.3, got@^8.2.0:
+got@^8.0.3:
version "8.3.0"
resolved "https://registry.yarnpkg.com/got/-/got-8.3.0.tgz#6ba26e75f8a6cc4c6b3eb1fe7ce4fec7abac8533"
dependencies:
@@ -3858,12 +3673,6 @@ graphlib@^2.1.1:
dependencies:
lodash "^4.11.1"
-grouped-queue@^0.3.3:
- version "0.3.3"
- resolved "https://registry.yarnpkg.com/grouped-queue/-/grouped-queue-0.3.3.tgz#c167d2a5319c5a0e0964ef6a25b7c2df8996c85c"
- dependencies:
- lodash "^4.17.2"
-
gzip-size@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-4.1.0.tgz#8ae096257eabe7d69c45be2b67c448124ffb517c"
@@ -3928,10 +3737,6 @@ has-binary2@~1.0.2:
dependencies:
isarray "2.0.1"
-has-color@~0.1.0:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/has-color/-/has-color-0.1.7.tgz#67144a5260c34fc3cca677d041daf52fe7b78b2f"
-
has-cors@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39"
@@ -4067,12 +3872,6 @@ 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"
@@ -4203,6 +4002,10 @@ icss-utils@^2.1.0:
dependencies:
postcss "^6.0.1"
+ieee754@^1.1.11:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.11.tgz#c16384ffe00f5b7835824e67b6f2bd44a5229455"
+
ieee754@^1.1.4:
version "1.1.8"
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4"
@@ -4215,7 +4018,7 @@ 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"
-ignore@^3.3.3, ignore@^3.3.5, ignore@^3.3.7:
+ignore@^3.3.3, ignore@^3.3.7:
version "3.3.8"
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b"
@@ -4251,10 +4054,6 @@ indent-string@^2.1.0:
dependencies:
repeating "^2.0.0"
-indent-string@^3.0.0:
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
-
indexes-of@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
@@ -4309,7 +4108,7 @@ inquirer@^3.0.6:
strip-ansi "^4.0.0"
through "^2.3.6"
-inquirer@^5.1.0, inquirer@^5.2.0:
+inquirer@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-5.2.0.tgz#db350c2b73daca77ff1243962e9f22f099685726"
dependencies:
@@ -4333,7 +4132,7 @@ internal-ip@1.2.0:
dependencies:
meow "^3.3.0"
-interpret@^1.0.0, interpret@^1.0.4:
+interpret@^1.0.0, interpret@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614"
@@ -4553,12 +4352,6 @@ is-object@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
-is-observable@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-0.2.0.tgz#b361311d83c6e5d726cabf5e250b0237106f5ae2"
- dependencies:
- symbol-observable "^0.2.2"
-
is-odd@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24"
@@ -4581,7 +4374,7 @@ is-path-inside@^1.0.0:
dependencies:
path-is-inside "^1.0.1"
-is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
+is-plain-obj@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
@@ -4627,12 +4420,6 @@ is-retry-allowed@^1.0.0, is-retry-allowed@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
-is-scoped@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-scoped/-/is-scoped-1.0.0.tgz#449ca98299e713038256289ecb2b540dc437cb30"
- dependencies:
- scoped-regex "^1.0.0"
-
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"
@@ -4655,7 +4442,7 @@ is-utf8@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
-is-windows@^1.0.1, is-windows@^1.0.2:
+is-windows@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
@@ -4675,7 +4462,7 @@ isarray@2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
-isbinaryfile@^3.0.0, isbinaryfile@^3.0.2:
+isbinaryfile@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.2.tgz#4a3e974ec0cba9004d3fc6cde7209ea69368a621"
@@ -4783,14 +4570,6 @@ istanbul@^0.4.5:
which "^1.1.1"
wordwrap "^1.0.0"
-istextorbinary@^2.2.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.2.1.tgz#a5231a08ef6dd22b268d0895084cf8d58b5bec53"
- dependencies:
- binaryextensions "2"
- editions "^1.3.3"
- textextensions "2"
-
isurl@^1.0.0-alpha5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
@@ -4854,46 +4633,6 @@ jsbn@~0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
-jscodeshift@^0.4.0:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.4.1.tgz#da91a1c2eccfa03a3387a21d39948e251ced444a"
- dependencies:
- async "^1.5.0"
- babel-plugin-transform-flow-strip-types "^6.8.0"
- babel-preset-es2015 "^6.9.0"
- babel-preset-stage-1 "^6.5.0"
- babel-register "^6.9.0"
- babylon "^6.17.3"
- colors "^1.1.2"
- flow-parser "^0.*"
- lodash "^4.13.1"
- micromatch "^2.3.7"
- node-dir "0.1.8"
- nomnom "^1.8.1"
- recast "^0.12.5"
- temp "^0.8.1"
- write-file-atomic "^1.2.0"
-
-jscodeshift@^0.5.0:
- version "0.5.0"
- resolved "https://registry.yarnpkg.com/jscodeshift/-/jscodeshift-0.5.0.tgz#bdb7b6cc20dd62c16aa728c3fa2d2fe66ca7c748"
- dependencies:
- babel-plugin-transform-flow-strip-types "^6.8.0"
- babel-preset-es2015 "^6.9.0"
- babel-preset-stage-1 "^6.5.0"
- babel-register "^6.9.0"
- babylon "^7.0.0-beta.30"
- colors "^1.1.2"
- flow-parser "^0.*"
- lodash "^4.13.1"
- micromatch "^2.3.7"
- neo-async "^2.5.0"
- node-dir "0.1.8"
- nomnom "^1.8.1"
- recast "^0.14.1"
- temp "^0.8.1"
- write-file-atomic "^1.2.0"
-
jsesc@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -4910,7 +4649,7 @@ json-buffer@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
-json-parse-better-errors@^1.0.1:
+json-parse-better-errors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
@@ -5108,6 +4847,10 @@ lcid@^1.0.0:
dependencies:
invert-kv "^1.0.0"
+leb@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/leb/-/leb-0.3.0.tgz#32bee9fad168328d6aea8522d833f4180eed1da3"
+
levn@^0.3.0, levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
@@ -5137,54 +4880,6 @@ lie@~3.1.0:
dependencies:
immediate "~3.0.5"
-listr-silent-renderer@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
-
-listr-update-renderer@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.4.0.tgz#344d980da2ca2e8b145ba305908f32ae3f4cc8a7"
- dependencies:
- chalk "^1.1.3"
- cli-truncate "^0.2.1"
- elegant-spinner "^1.0.1"
- figures "^1.7.0"
- indent-string "^3.0.0"
- log-symbols "^1.0.2"
- log-update "^1.0.2"
- strip-ansi "^3.0.1"
-
-listr-verbose-renderer@^0.4.0:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.4.1.tgz#8206f4cf6d52ddc5827e5fd14989e0e965933a35"
- dependencies:
- chalk "^1.1.3"
- cli-cursor "^1.0.2"
- date-fns "^1.27.2"
- figures "^1.7.0"
-
-listr@^0.13.0:
- version "0.13.0"
- resolved "https://registry.yarnpkg.com/listr/-/listr-0.13.0.tgz#20bb0ba30bae660ee84cc0503df4be3d5623887d"
- dependencies:
- chalk "^1.1.3"
- cli-truncate "^0.2.1"
- figures "^1.7.0"
- indent-string "^2.1.0"
- is-observable "^0.2.0"
- is-promise "^2.1.0"
- is-stream "^1.1.0"
- listr-silent-renderer "^1.1.1"
- listr-update-renderer "^0.4.0"
- listr-verbose-renderer "^0.4.0"
- log-symbols "^1.0.2"
- log-update "^1.0.2"
- ora "^0.2.3"
- p-map "^1.1.1"
- rxjs "^5.4.2"
- stream-to-observable "^0.2.0"
- strip-ansi "^3.0.1"
-
load-json-file@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
@@ -5204,15 +4899,6 @@ load-json-file@^2.0.0:
pify "^2.0.0"
strip-bom "^3.0.0"
-load-json-file@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b"
- dependencies:
- graceful-fs "^4.1.2"
- parse-json "^4.0.0"
- pify "^3.0.0"
- strip-bom "^3.0.0"
-
loader-runner@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2"
@@ -5272,29 +4958,16 @@ lodash@4.17.4:
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
-lodash@^4.0.0, lodash@^4.11.1, lodash@^4.13.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
+lodash@^4.0.0, lodash@^4.11.1, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.0, lodash@^4.3.0, lodash@^4.5.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
-log-symbols@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
- dependencies:
- chalk "^1.0.0"
-
-log-symbols@^2.1.0, log-symbols@^2.2.0:
+log-symbols@^2.1.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a"
dependencies:
chalk "^2.0.1"
-log-update@^1.0.2:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/log-update/-/log-update-1.0.2.tgz#19929f64c4093d2d2e7075a1dad8af59c296b8d1"
- dependencies:
- ansi-escapes "^1.0.0"
- cli-cursor "^1.0.2"
-
log4js@^2.3.9:
version "2.5.3"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-2.5.3.tgz#38bb7bde5e9c1c181bd75e8bc128c5cd0409caf1"
@@ -5330,6 +5003,10 @@ loglevelnext@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/loglevelnext/-/loglevelnext-1.0.3.tgz#0f69277e73bbbf2cd61b94d82313216bf87ac66e"
+long@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
+
longest@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
@@ -5391,12 +5068,16 @@ mailgun-js@^0.7.0:
q "~1.4.0"
tsscmp "~1.0.0"
-make-dir@^1.0.0, make-dir@^1.1.0:
+make-dir@^1.0.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b"
dependencies:
pify "^3.0.0"
+mamacro@^0.0.3:
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4"
+
map-cache@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@@ -5438,30 +5119,6 @@ media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
-mem-fs-editor@^4.0.0:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/mem-fs-editor/-/mem-fs-editor-4.0.1.tgz#27e6b59df91b37248e9be2145b1bea84695103ed"
- dependencies:
- commondir "^1.0.1"
- deep-extend "^0.5.1"
- ejs "^2.5.9"
- glob "^7.0.3"
- globby "^8.0.0"
- isbinaryfile "^3.0.2"
- mkdirp "^0.5.0"
- multimatch "^2.0.0"
- rimraf "^2.2.8"
- through2 "^2.0.0"
- vinyl "^2.0.1"
-
-mem-fs@^1.1.0:
- version "1.1.3"
- resolved "https://registry.yarnpkg.com/mem-fs/-/mem-fs-1.1.3.tgz#b8ae8d2e3fcb6f5d3f9165c12d4551a065d989cc"
- dependencies:
- through2 "^2.0.0"
- vinyl "^1.1.0"
- vinyl-file "^2.0.0"
-
mem@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
@@ -5504,15 +5161,11 @@ merge-source-map@^1.1.0:
dependencies:
source-map "^0.6.1"
-merge2@^1.2.1:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34"
-
methods@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
-micromatch@^2.1.5, micromatch@^2.3.7:
+micromatch@^2.1.5:
version "2.3.11"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
dependencies:
@@ -5530,7 +5183,7 @@ micromatch@^2.1.5, micromatch@^2.3.7:
parse-glob "^3.0.4"
regex-cache "^0.4.2"
-micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
+micromatch@^3.1.4, micromatch@^3.1.8, micromatch@^3.1.9:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
dependencies:
@@ -5603,10 +5256,6 @@ minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
-minimist@^0.1.0:
- version "0.1.0"
- resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de"
-
minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
@@ -5637,7 +5286,7 @@ mixin-deep@^1.2.0:
for-in "^1.0.2"
is-extendable "^1.0.1"
-mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
+mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
dependencies:
@@ -5647,9 +5296,13 @@ moment@2.x, moment@^2.18.1:
version "2.19.2"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.2.tgz#8a7f774c95a64550b4c7ebd496683908f9419dbe"
-monaco-editor@0.10.0:
- version "0.10.0"
- resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.10.0.tgz#6604932585fe9c1f993f000a503d0d20fbe5896a"
+monaco-editor-webpack-plugin@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.2.1.tgz#577ed091420f422bb8f0ff3a8899dd82344da56d"
+
+monaco-editor@0.13.1:
+ version "0.13.1"
+ resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.13.1.tgz#6b9ce20e4d1c945042d256825eb133cb23315a52"
mousetrap@^1.4.6:
version "1.4.6"
@@ -5685,15 +5338,6 @@ multicast-dns@^6.0.1:
dns-packet "^1.0.1"
thunky "^0.1.0"
-multimatch@^2.0.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-2.1.0.tgz#9c7906a22fb4c02919e2f5f75161b4cdbd4b2a2b"
- dependencies:
- array-differ "^1.0.0"
- array-union "^1.0.1"
- arrify "^1.0.0"
- minimatch "^3.0.0"
-
mute-stream@0.0.7:
version "0.0.7"
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
@@ -5739,10 +5383,6 @@ nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
-node-dir@0.1.8:
- version "0.1.8"
- resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.8.tgz#55fb8deb699070707fb67f91a460f0448294c77d"
-
node-forge@0.6.33:
version "0.6.33"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
@@ -5859,13 +5499,6 @@ nodemon@^1.17.3:
undefsafe "^2.0.2"
update-notifier "^2.3.0"
-nomnom@^1.8.1:
- version "1.8.1"
- resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.8.1.tgz#2151f722472ba79e50a76fc125bb8c8f2e4dc2a7"
- dependencies:
- chalk "~0.4.0"
- underscore "~1.6.0"
-
nopt@3.x:
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
@@ -6011,10 +5644,6 @@ once@1.x, once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0:
dependencies:
wrappy "1"
-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"
@@ -6049,15 +5678,6 @@ optionator@^0.8.1, optionator@^0.8.2:
type-check "~0.3.2"
wordwrap "~1.0.0"
-ora@^0.2.3:
- version "0.2.3"
- resolved "https://registry.yarnpkg.com/ora/-/ora-0.2.3.tgz#37527d220adcd53c39b73571d754156d5db657a4"
- dependencies:
- chalk "^1.1.1"
- cli-cursor "^1.0.2"
- cli-spinners "^0.1.2"
- object-assign "^4.0.1"
-
original@>=0.0.5:
version "1.0.0"
resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b"
@@ -6091,20 +5711,10 @@ osenv@^0.1.4:
os-homedir "^1.0.0"
os-tmpdir "^1.0.0"
-p-cancelable@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
-
p-cancelable@^0.4.0:
version "0.4.1"
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0"
-p-each-series@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
- dependencies:
- p-reduce "^1.0.0"
-
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
@@ -6113,11 +5723,7 @@ p-is-promise@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
-p-lazy@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-lazy/-/p-lazy-1.0.0.tgz#ec53c802f2ee3ac28f166cc82d0b2b02de27a835"
-
-p-limit@^1.0.0, p-limit@^1.1.0:
+p-limit@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c"
dependencies:
@@ -6133,16 +5739,6 @@ p-map@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a"
-p-reduce@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
-
-p-timeout@^1.1.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386"
- dependencies:
- p-finally "^1.0.0"
-
p-timeout@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038"
@@ -6223,17 +5819,6 @@ parse-json@^2.2.0:
dependencies:
error-ex "^1.2.0"
-parse-json@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0"
- dependencies:
- error-ex "^1.3.1"
- json-parse-better-errors "^1.0.1"
-
-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"
@@ -6312,12 +5897,6 @@ 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"
@@ -6342,7 +5921,7 @@ performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
-pify@^2.0.0, pify@^2.3.0:
+pify@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
@@ -6672,25 +6251,21 @@ prettier@1.11.1:
version "1.11.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.11.1.tgz#61e43fc4cd44e68f2b0dfc2c38cd4bb0fccdcc75"
-prettier@^1.11.1, prettier@^1.5.3:
+prettier@^1.11.1:
version "1.12.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.12.1.tgz#c1ad20e803e7749faf905a409d2367e06bbe7325"
-pretty-bytes@^4.0.2:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9"
-
prismjs@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.6.0.tgz#118d95fb7a66dba2272e343b345f5236659db365"
optionalDependencies:
clipboard "^1.5.5"
-private@^0.1.6, private@^0.1.8, private@~0.1.5:
+private@^0.1.6, private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
-process-nextick-args@^1.0.6, process-nextick-args@~1.0.6:
+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"
@@ -6898,13 +6473,6 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7:
minimist "^1.2.0"
strip-json-comments "~2.0.1"
-read-chunk@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/read-chunk/-/read-chunk-2.1.0.tgz#6a04c0928005ed9d42e1a6ac5600e19cbc7ff655"
- dependencies:
- pify "^3.0.0"
- safe-buffer "^5.1.1"
-
read-pkg-up@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02"
@@ -6919,13 +6487,6 @@ read-pkg-up@^2.0.0:
find-up "^2.0.0"
read-pkg "^2.0.0"
-read-pkg-up@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07"
- dependencies:
- find-up "^2.0.0"
- read-pkg "^3.0.0"
-
read-pkg@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28"
@@ -6942,14 +6503,6 @@ read-pkg@^2.0.0:
normalize-package-data "^2.3.2"
path-type "^2.0.0"
-read-pkg@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389"
- dependencies:
- load-json-file "^4.0.0"
- normalize-package-data "^2.3.2"
- path-type "^3.0.0"
-
"readable-stream@1 || 2", readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, 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.2.2, readable-stream@^2.2.9, 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"
@@ -7003,31 +6556,6 @@ readdirp@^2.0.0:
readable-stream "^2.0.2"
set-immediate-shim "^1.0.1"
-recast@^0.12.5:
- version "0.12.9"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.12.9.tgz#e8e52bdb9691af462ccbd7c15d5a5113647a15f1"
- dependencies:
- ast-types "0.10.1"
- core-js "^2.4.1"
- esprima "~4.0.0"
- private "~0.1.5"
- source-map "~0.6.1"
-
-recast@^0.14.1:
- version "0.14.7"
- resolved "https://registry.yarnpkg.com/recast/-/recast-0.14.7.tgz#4f1497c2b5826d42a66e8e3c9d80c512983ff61d"
- dependencies:
- ast-types "0.11.3"
- esprima "~4.0.0"
- private "~0.1.5"
- source-map "~0.6.1"
-
-rechoir@^0.6.2:
- version "0.6.2"
- resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
- dependencies:
- resolve "^1.1.6"
-
redent@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
@@ -7155,14 +6683,6 @@ repeating@^2.0.0:
dependencies:
is-finite "^1.0.0"
-replace-ext@0.0.1:
- version "0.0.1"
- resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924"
-
-replace-ext@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
-
request@2.75.x:
version "2.75.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.75.0.tgz#d2b8268a286da13eaa5d01adf5d18cc90f657d93"
@@ -7252,10 +6772,6 @@ requestretry@^1.2.2:
request "^2.74.0"
when "^3.7.7"
-require-all@^2.2.0:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/require-all/-/require-all-2.2.0.tgz#b4420c233ac0282d0ff49b277fb880a8b5de0894"
-
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -7281,13 +6797,6 @@ resolve-cwd@^2.0.0:
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"
@@ -7304,7 +6813,7 @@ resolve@1.1.x:
version "1.1.7"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
-resolve@^1.1.6, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
+resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
version "1.7.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.7.1.tgz#aadd656374fd298aee895bc026b8297418677fd3"
dependencies:
@@ -7316,13 +6825,6 @@ responselike@1.0.2:
dependencies:
lowercase-keys "^1.0.0"
-restore-cursor@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
- dependencies:
- exit-hook "^1.0.0"
- onetime "^1.0.0"
-
restore-cursor@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
@@ -7346,10 +6848,6 @@ rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.
dependencies:
glob "^7.0.5"
-rimraf@~2.2.6:
- version "2.2.8"
- resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582"
-
ripemd160@^2.0.0, ripemd160@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7"
@@ -7357,7 +6855,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
hash-base "^2.0.0"
inherits "^2.0.1"
-run-async@^2.0.0, run-async@^2.2.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:
@@ -7379,7 +6877,7 @@ rx-lite@*, rx-lite@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444"
-rxjs@^5.4.2, rxjs@^5.5.2:
+rxjs@^5.5.2:
version "5.5.10"
resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.10.tgz#fde02d7a614f6c8683d0d1957827f492e09db045"
dependencies:
@@ -7418,10 +6916,6 @@ schema-utils@^0.4.0, schema-utils@^0.4.2, schema-utils@^0.4.3, schema-utils@^0.4
ajv "^6.1.0"
ajv-keywords "^3.1.0"
-scoped-regex@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/scoped-regex/-/scoped-regex-1.0.0.tgz#a346bb1acd4207ae70bd7c0c7ca9e566b6baddb8"
-
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -7565,14 +7059,6 @@ shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
-shelljs@^0.8.0:
- version "0.8.1"
- resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.1.tgz#729e038c413a2254c4078b95ed46e0397154a9f1"
- dependencies:
- glob "^7.0.0"
- interpret "^1.0.0"
- rechoir "^0.6.2"
-
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"
@@ -7587,20 +7073,12 @@ slash@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
-slice-ansi@0.0.4:
- version "0.0.4"
- resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
-
slice-ansi@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d"
dependencies:
is-fullwidth-code-point "^2.0.0"
-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"
@@ -7937,12 +7415,6 @@ stream-shift@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952"
-stream-to-observable@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/stream-to-observable/-/stream-to-observable-0.2.0.tgz#59d6ea393d87c2c0ddac10aa0d561bc6ba6f0e10"
- dependencies:
- any-observable "^0.2.0"
-
streamroller@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-0.7.0.tgz#a1d1b7cf83d39afb0d63049a5acbf93493bdf64b"
@@ -7956,10 +7428,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-template@~0.2.1:
- version "0.2.1"
- resolved "https://registry.yarnpkg.com/string-template/-/string-template-0.2.1.tgz#42932e598a352d01fc22ec3367d9d84eec6c9add"
-
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"
@@ -8007,17 +7475,6 @@ strip-ansi@^4.0.0:
dependencies:
ansi-regex "^3.0.0"
-strip-ansi@~0.1.0:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991"
-
-strip-bom-stream@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca"
- dependencies:
- first-chunk-stream "^2.0.0"
- strip-bom "^2.0.0"
-
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -8085,10 +7542,6 @@ symbol-observable@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
-symbol-observable@^0.2.2:
- version "0.2.4"
- resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-0.2.4.tgz#95a83db26186d6af7e7a18dbd9760a2f86d08f40"
-
table@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
@@ -8129,13 +7582,6 @@ tar@^2.2.1:
fstream "^1.0.2"
inherits "2"
-temp@^0.8.1:
- version "0.8.3"
- resolved "https://registry.yarnpkg.com/temp/-/temp-0.8.3.tgz#e0c6bc4d26b903124410e4fed81103014dfc1f59"
- dependencies:
- os-tmpdir "^1.0.0"
- rimraf "~2.2.6"
-
term-size@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69"
@@ -8152,14 +7598,10 @@ test-exclude@^4.2.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"
-textextensions@2:
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.2.0.tgz#38ac676151285b658654581987a0ce1a4490d286"
-
three-orbit-controls@^82.1.0:
version "82.1.0"
resolved "https://registry.yarnpkg.com/three-orbit-controls/-/three-orbit-controls-82.1.0.tgz#11a7f33d0a20ecec98f098b37780f6537374fab4"
@@ -8172,7 +7614,7 @@ three@^0.84.0:
version "0.84.0"
resolved "https://registry.yarnpkg.com/three/-/three-0.84.0.tgz#95be85a55a0fa002aa625ed559130957dcffd918"
-through2@^2.0.0, through2@^2.0.1:
+through2@^2.0.0:
version "2.0.3"
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be"
dependencies:
@@ -8381,10 +7823,6 @@ underscore@^1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.0.tgz#31dbb314cfcc88f169cd3692d9149d81a00a73e4"
-underscore@~1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
-
underscore@~1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209"
@@ -8441,10 +7879,6 @@ unset-value@^1.0.0:
has-value "^0.3.1"
isobject "^3.0.0"
-untildify@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/untildify/-/untildify-3.0.2.tgz#7f1f302055b3fea0f3e81dc78eb36766cb65e3f1"
-
unzip-response@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/unzip-response/-/unzip-response-2.0.1.tgz#d2f0f737d16b0615e72a6935ed04214572d56f97"
@@ -8561,9 +7995,9 @@ uws@~9.14.0:
version "9.14.0"
resolved "https://registry.yarnpkg.com/uws/-/uws-9.14.0.tgz#fac8386befc33a7a3705cbd58dc47b430ca4dd95"
-v8-compile-cache@^1.1.2:
- version "1.1.2"
- resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-1.1.2.tgz#8d32e4f16974654657e676e0e467a348e89b0dc4"
+v8-compile-cache@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a"
validate-npm-package-license@^3.0.1:
version "3.0.1"
@@ -8592,36 +8026,6 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
-vinyl-file@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/vinyl-file/-/vinyl-file-2.0.0.tgz#a7ebf5ffbefda1b7d18d140fcb07b223efb6751a"
- dependencies:
- graceful-fs "^4.1.2"
- pify "^2.3.0"
- pinkie-promise "^2.0.0"
- strip-bom "^2.0.0"
- strip-bom-stream "^2.0.0"
- vinyl "^1.1.0"
-
-vinyl@^1.1.0:
- version "1.2.0"
- resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-1.2.0.tgz#5c88036cf565e5df05558bfc911f8656df218884"
- dependencies:
- clone "^1.0.0"
- clone-stats "^0.0.1"
- replace-ext "0.0.1"
-
-vinyl@^2.0.1:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c"
- dependencies:
- clone "^2.1.1"
- clone-buffer "^1.0.0"
- clone-stats "^1.0.0"
- cloneable-readable "^1.0.0"
- remove-trailing-separator "^1.0.1"
- replace-ext "^1.0.0"
-
visibilityjs@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/visibilityjs/-/visibilityjs-1.2.4.tgz#bff8663da62c8c10ad4ee5ae6a1ae6fac4259d63"
@@ -8636,7 +8040,7 @@ void-elements@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
-vue-eslint-parser@^2.0.1:
+vue-eslint-parser@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1"
dependencies:
@@ -8715,12 +8119,6 @@ wbuf@^1.1.0, wbuf@^1.7.2:
dependencies:
minimalistic-assert "^1.0.0"
-webpack-addons@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/webpack-addons/-/webpack-addons-1.1.5.tgz#2b178dfe873fb6e75e40a819fa5c26e4a9bc837a"
- dependencies:
- jscodeshift "^0.4.0"
-
webpack-bundle-analyzer@^2.11.1:
version "2.11.1"
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.11.1.tgz#b9fbfb6a32c0a8c1c3237223e90890796b950ab9"
@@ -8738,36 +8136,21 @@ webpack-bundle-analyzer@^2.11.1:
opener "^1.4.3"
ws "^4.0.0"
-webpack-cli@^2.1.2:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-2.1.2.tgz#9c9a4b90584f7b8acaf591238ef0667e04c817f6"
+webpack-cli@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.0.2.tgz#e48c5662aff8ed5aac3db5f82f51d7f32e50459e"
dependencies:
- chalk "^2.3.2"
+ chalk "^2.4.1"
cross-spawn "^6.0.5"
- diff "^3.5.0"
enhanced-resolve "^4.0.0"
- envinfo "^4.4.2"
- glob-all "^3.1.0"
- global-modules "^1.0.0"
- got "^8.2.0"
+ global-modules-path "^2.1.0"
import-local "^1.0.0"
- inquirer "^5.1.0"
- interpret "^1.0.4"
- jscodeshift "^0.5.0"
- listr "^0.13.0"
+ inquirer "^5.2.0"
+ interpret "^1.1.0"
loader-utils "^1.1.0"
- lodash "^4.17.5"
- log-symbols "^2.2.0"
- mkdirp "^0.5.1"
- p-each-series "^1.0.0"
- p-lazy "^1.0.0"
- prettier "^1.5.3"
- supports-color "^5.3.0"
- v8-compile-cache "^1.1.2"
- webpack-addons "^1.1.5"
+ supports-color "^5.4.0"
+ v8-compile-cache "^2.0.0"
yargs "^11.1.0"
- yeoman-environment "^2.0.0"
- yeoman-generator "^2.0.4"
webpack-dev-middleware@3.1.3:
version "3.1.3"
@@ -8846,10 +8229,15 @@ webpack-stats-plugin@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/webpack-stats-plugin/-/webpack-stats-plugin-0.2.1.tgz#1f5bac13fc25d62cbb5fd0ff646757dc802b8595"
-webpack@^4.7.0:
- version "4.7.0"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.7.0.tgz#a04f68dab86d5545fd0277d07ffc44e4078154c9"
+webpack@^4.11.1:
+ version "4.11.1"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.11.1.tgz#1aa0b936f7ae93a52cf38d2ad0d0f46dcf3c2723"
dependencies:
+ "@webassemblyjs/ast" "1.5.10"
+ "@webassemblyjs/helper-module-context" "1.5.10"
+ "@webassemblyjs/wasm-edit" "1.5.10"
+ "@webassemblyjs/wasm-opt" "1.5.10"
+ "@webassemblyjs/wasm-parser" "1.5.10"
acorn "^5.0.0"
acorn-dynamic-import "^3.0.0"
ajv "^6.1.0"
@@ -8857,6 +8245,7 @@ webpack@^4.7.0:
chrome-trace-event "^0.1.1"
enhanced-resolve "^4.0.0"
eslint-scope "^3.7.1"
+ json-parse-better-errors "^1.0.2"
loader-runner "^2.3.0"
loader-utils "^1.1.0"
memory-fs "~0.4.1"
@@ -8892,7 +8281,7 @@ which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
-which@^1.1.1, which@^1.2.1, which@^1.2.14, which@^1.2.9:
+which@^1.1.1, which@^1.2.1, which@^1.2.9:
version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
dependencies:
@@ -8951,14 +8340,6 @@ wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
-write-file-atomic@^1.2.0:
- version "1.3.4"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-1.3.4.tgz#f807a4f0b1d9e913ae7a48112e6cc3af1991b45f"
- dependencies:
- graceful-fs "^4.1.11"
- imurmurhash "^0.1.4"
- slide "^1.1.5"
-
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"
@@ -9001,7 +8382,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.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"
@@ -9057,12 +8438,6 @@ yargs@^11.1.0:
y18n "^3.2.1"
yargs-parser "^9.0.2"
-yargs@~1.2.6:
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b"
- dependencies:
- minimist "^0.1.0"
-
yargs@~3.10.0:
version "3.10.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"
@@ -9075,53 +8450,3 @@ yargs@~3.10.0:
yeast@0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
-
-yeoman-environment@^2.0.0, yeoman-environment@^2.0.5:
- version "2.1.1"
- resolved "https://registry.yarnpkg.com/yeoman-environment/-/yeoman-environment-2.1.1.tgz#10a045f7fc4397873764882eae055a33e56ee1c5"
- dependencies:
- chalk "^2.1.0"
- cross-spawn "^6.0.5"
- debug "^3.1.0"
- diff "^3.3.1"
- escape-string-regexp "^1.0.2"
- globby "^8.0.1"
- grouped-queue "^0.3.3"
- inquirer "^5.2.0"
- is-scoped "^1.0.0"
- lodash "^4.17.10"
- log-symbols "^2.1.0"
- mem-fs "^1.1.0"
- strip-ansi "^4.0.0"
- text-table "^0.2.0"
- untildify "^3.0.2"
-
-yeoman-generator@^2.0.4:
- version "2.0.5"
- resolved "https://registry.yarnpkg.com/yeoman-generator/-/yeoman-generator-2.0.5.tgz#57b0b3474701293cc9ec965288f3400b00887c81"
- dependencies:
- async "^2.6.0"
- chalk "^2.3.0"
- cli-table "^0.3.1"
- cross-spawn "^6.0.5"
- dargs "^5.1.0"
- dateformat "^3.0.3"
- debug "^3.1.0"
- detect-conflict "^1.0.0"
- error "^7.0.2"
- find-up "^2.1.0"
- github-username "^4.0.0"
- istextorbinary "^2.2.1"
- lodash "^4.17.10"
- make-dir "^1.1.0"
- mem-fs-editor "^4.0.0"
- minimist "^1.2.0"
- pretty-bytes "^4.0.2"
- read-chunk "^2.1.0"
- read-pkg-up "^3.0.0"
- rimraf "^2.6.2"
- run-async "^2.0.0"
- shelljs "^0.8.0"
- text-table "^0.2.0"
- through2 "^2.0.0"
- yeoman-environment "^2.0.5"