summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-03-27 12:35:10 +0200
committerGrzegorz Bizon <grzesiek.bizon@gmail.com>2017-03-27 12:35:10 +0200
commit1a4c60ef57d047dab6aa823f7cc50548897b74f6 (patch)
tree92bbccd1e1850837da5c7b10344ff77f0be0e7be
parent7ada193e0fd28b4a6eca1fda7dda6f0ebe6b2d72 (diff)
parent7324d6713262d7f9c563d48b82934c4a8eb72a52 (diff)
downloadgitlab-ce-1a4c60ef57d047dab6aa823f7cc50548897b74f6.tar.gz
Merge branch 'master' into feature/multi-level-container-registry-images
* master: (192 commits) Implement new service for creating user Update sentry-raven 2.0.2 -> 2.4.0 Update webmock 1.21.0 -> 1.24.6 Update spring 1.7.2 -> 2.0.1 Update simplecov 0.12.0 -> 0.14.1 Update pry-rails 0.3.4 -> 0.3.5 Update pry-byebug 3.4.1 -> 3.4.2 Update flay 2.6.1 -> 2.8.1 Remove Tags filter from Projects Explore dropdown Update capybara-screenshot 1.0.11 -> 1.0.14 Update bullet 5.2.0 -> 5.5.1 Update brakeman 3.4.1 -> 3.6.1 Remove web-console gem Update better_errors 1.0.1 -> 2.1.1 Display flash message to unauthenticated user when creating new issue Fix up emoji tests that should have failed :/ Fix RSpec/DescribeSymbol cop violations Add event limit warning all tabs Cycle Analytics Adding non_archived scope for counting projects Resolve "Gitlab administrator cannot create projects in every group" ... Conflicts: db/schema.rb
-rw-r--r--.gitlab-ci.yml12
-rw-r--r--CHANGELOG.md283
-rw-r--r--CONTRIBUTING.md8
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile18
-rw-r--r--Gemfile.lock64
-rw-r--r--README.md7
-rw-r--r--app/assets/images/icon-merge-request-unmerged.svg2
-rw-r--r--app/assets/javascripts/activities.js3
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/behaviors/toggler_behavior.js2
-rw-r--r--app/assets/javascripts/blob/blob_ci_yaml.js42
-rw-r--r--app/assets/javascripts/blob/blob_dockerfile_selector.js19
-rw-r--r--app/assets/javascripts/blob/blob_dockerfile_selectors.js27
-rw-r--r--app/assets/javascripts/blob/blob_file_dropzone.js121
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selector.js23
-rw-r--r--app/assets/javascripts/blob/blob_gitignore_selectors.js26
-rw-r--r--app/assets/javascripts/blob/blob_license_selector.js28
-rw-r--r--app/assets/javascripts/blob/blob_license_selectors.js23
-rw-r--r--app/assets/javascripts/blob/template_selector.js101
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selector.js9
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selectors.js23
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_dockerfile_selector.js9
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_dockerfile_selectors.js23
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_gitignore_selector.js9
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_gitignore_selectors.js23
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_license_selector.js13
-rw-r--r--app/assets/javascripts/blob/template_selectors/blob_license_selectors.js24
-rw-r--r--app/assets/javascripts/blob/template_selectors/template_selector.js92
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js32
-rw-r--r--app/assets/javascripts/blob_edit/blob_edit_bundle.js15
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js177
-rw-r--r--app/assets/javascripts/boards/boards_bundle.js7
-rw-r--r--app/assets/javascripts/boards/components/board.js2
-rw-r--r--app/assets/javascripts/boards/components/board_blank_state.js4
-rw-r--r--app/assets/javascripts/boards/components/board_card.js1
-rw-r--r--app/assets/javascripts/boards/components/board_delete.js3
-rw-r--r--app/assets/javascripts/boards/components/board_list.js4
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js3
-rw-r--r--app/assets/javascripts/boards/components/issue_card_inner.js2
-rw-r--r--app/assets/javascripts/boards/components/modal/empty_state.js3
-rw-r--r--app/assets/javascripts/boards/components/modal/filters.js4
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.js3
-rw-r--r--app/assets/javascripts/boards/components/modal/index.js18
-rw-r--r--app/assets/javascripts/boards/components/modal/list.js4
-rw-r--r--app/assets/javascripts/boards/components/modal/lists_dropdown.js3
-rw-r--r--app/assets/javascripts/boards/components/modal/tabs.js3
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.js4
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js2
-rw-r--r--app/assets/javascripts/boards/filters/due_date_filters.js3
-rw-r--r--app/assets/javascripts/boards/models/issue.js3
-rw-r--r--app/assets/javascripts/boards/services/board_service.js3
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js3
-rw-r--r--app/assets/javascripts/boards/stores/modal_store.js1
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_bundle.js6
-rw-r--r--app/assets/javascripts/commit/pipelines/pipelines_table.js35
-rw-r--r--app/assets/javascripts/commons/polyfills.js2
-rw-r--r--app/assets/javascripts/cycle_analytics/components/limit_warning_component.js17
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_code_component.js4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_issue_component.js4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_plan_component.js7
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_production_component.js4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_review_component.js4
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_staging_component.js1
-rw-r--r--app/assets/javascripts/cycle_analytics/components/stage_test_component.js1
-rw-r--r--app/assets/javascripts/cycle_analytics/components/total_time_component.js3
-rw-r--r--app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js9
-rw-r--r--app/assets/javascripts/diff.js11
-rw-r--r--app/assets/javascripts/diff_notes/components/comment_resolve_btn.js3
-rw-r--r--app/assets/javascripts/diff_notes/components/diff_note_avatars.js4
-rw-r--r--app/assets/javascripts/diff_notes/components/jump_to_discussion.js3
-rw-r--r--app/assets/javascripts/diff_notes/components/new_issue_for_discussion.js3
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_btn.js3
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_count.js3
-rw-r--r--app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js2
-rw-r--r--app/assets/javascripts/diff_notes/diff_notes_bundle.js4
-rw-r--r--app/assets/javascripts/diff_notes/models/discussion.js3
-rw-r--r--app/assets/javascripts/diff_notes/services/resolve.js7
-rw-r--r--app/assets/javascripts/diff_notes/stores/comments.js3
-rw-r--r--app/assets/javascripts/dispatcher.js2
-rw-r--r--app/assets/javascripts/droplab/droplab_filter.js2
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js2
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js2
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/comparison_pane.js3
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/estimate_only_pane.js3
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/help_state.js3
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/no_tracking_pane.js3
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/spent_only_pane.js3
-rw-r--r--app/assets/javascripts/issuable/time_tracking/components/time_tracker.js2
-rw-r--r--app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js7
-rw-r--r--app/assets/javascripts/issuable_context.js3
-rw-r--r--app/assets/javascripts/lib/utils/poll.js73
-rw-r--r--app/assets/javascripts/main.js11
-rw-r--r--app/assets/javascripts/merge_conflicts/components/diff_file_editor.js3
-rw-r--r--app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js3
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js3
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflict_store.js5
-rw-r--r--app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js4
-rw-r--r--app/assets/javascripts/merge_request_tabs.js7
-rw-r--r--app/assets/javascripts/milestone_select.js3
-rw-r--r--app/assets/javascripts/monitoring/prometheus_graph.js14
-rw-r--r--app/assets/javascripts/notes.js4
-rw-r--r--app/assets/javascripts/project.js3
-rw-r--r--app/assets/javascripts/render_gfm.js3
-rw-r--r--app/assets/javascripts/render_math.js4
-rw-r--r--app/assets/javascripts/right_sidebar.js5
-rw-r--r--app/assets/javascripts/shortcuts.js4
-rw-r--r--app/assets/javascripts/shortcuts_dashboard_navigation.js3
-rw-r--r--app/assets/javascripts/shortcuts_navigation.js3
-rw-r--r--app/assets/javascripts/subscription.js2
-rw-r--r--app/assets/javascripts/templates/issuable_template_selector.js14
-rw-r--r--app/assets/javascripts/user.js3
-rw-r--r--app/assets/javascripts/user_callout.js6
-rw-r--r--app/assets/javascripts/user_tabs.js21
-rw-r--r--app/assets/javascripts/users_select.js3
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/empty_state.js33
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/error_state.js19
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/nav_controls.js52
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js68
-rw-r--r--app/assets/javascripts/vue_pipelines_index/index.js12
-rw-r--r--app/assets/javascripts/vue_pipelines_index/pipelines.js191
-rw-r--r--app/assets/javascripts/vue_shared/common_vue.js6
-rw-r--r--app/assets/javascripts/vue_shared/vue_resource_interceptor.js11
-rw-r--r--app/assets/stylesheets/framework/awards.scss9
-rw-r--r--app/assets/stylesheets/framework/buttons.scss27
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss2
-rw-r--r--app/assets/stylesheets/framework/forms.scss22
-rw-r--r--app/assets/stylesheets/framework/header.scss2
-rw-r--r--app/assets/stylesheets/framework/icons.scss12
-rw-r--r--app/assets/stylesheets/framework/issue_box.scss2
-rw-r--r--app/assets/stylesheets/framework/layout.scss24
-rw-r--r--app/assets/stylesheets/framework/lists.scss2
-rw-r--r--app/assets/stylesheets/framework/nav.scss12
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap.scss4
-rw-r--r--app/assets/stylesheets/framework/tw_bootstrap_variables.scss7
-rw-r--r--app/assets/stylesheets/framework/variables.scss221
-rw-r--r--app/assets/stylesheets/pages/boards.scss4
-rw-r--r--app/assets/stylesheets/pages/builds.scss6
-rw-r--r--app/assets/stylesheets/pages/environments.scss5
-rw-r--r--app/assets/stylesheets/pages/issuable.scss6
-rw-r--r--app/assets/stylesheets/pages/issues.scss18
-rw-r--r--app/assets/stylesheets/pages/login.scss4
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss2
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss2
-rw-r--r--app/assets/stylesheets/pages/milestone.scss2
-rw-r--r--app/assets/stylesheets/pages/notes.scss7
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss64
-rw-r--r--app/assets/stylesheets/pages/profile.scss1
-rw-r--r--app/assets/stylesheets/pages/projects.scss69
-rw-r--r--app/assets/stylesheets/pages/sherlock.scss2
-rw-r--r--app/assets/stylesheets/pages/status.scss68
-rw-r--r--app/assets/stylesheets/pages/todos.scss30
-rw-r--r--app/controllers/admin/users_controller.rb12
-rw-r--r--app/controllers/application_controller.rb7
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/controllers/import/bitbucket_controller.rb8
-rw-r--r--app/controllers/projects/application_controller.rb5
-rw-r--r--app/controllers/projects/blob_controller.rb5
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb4
-rw-r--r--app/controllers/projects/issues_controller.rb18
-rwxr-xr-x[-rw-r--r--]app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/projects/milestones_controller.rb9
-rw-r--r--app/controllers/projects/settings/members_controller.rb1
-rw-r--r--app/controllers/projects/tree_controller.rb1
-rw-r--r--app/controllers/registrations_controller.rb11
-rw-r--r--app/controllers/users_controller.rb4
-rw-r--r--app/finders/todos_finder.rb12
-rw-r--r--app/helpers/milestones_helper.rb2
-rw-r--r--app/helpers/namespaces_helper.rb8
-rw-r--r--app/helpers/nav_helper.rb6
-rw-r--r--app/helpers/sorting_helper.rb28
-rw-r--r--app/helpers/todos_helper.rb10
-rw-r--r--app/helpers/users_helper.rb7
-rw-r--r--app/models/concerns/issuable.rb1
-rw-r--r--app/models/concerns/spammable.rb2
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/issue.rb3
-rw-r--r--app/models/merge_request.rb11
-rw-r--r--app/models/milestone.rb15
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/project.rb15
-rw-r--r--app/models/project_services/chat_notification_service.rb27
-rw-r--r--app/models/project_services/mattermost_service.rb16
-rw-r--r--app/models/project_services/prometheus_service.rb17
-rw-r--r--app/models/project_services/slack_service.rb16
-rw-r--r--app/models/route.rb4
-rw-r--r--app/models/snippet.rb3
-rw-r--r--app/models/user.rb27
-rw-r--r--app/services/boards/issues/list_service.rb2
-rw-r--r--app/services/create_branch_service.rb6
-rw-r--r--app/services/spam_check_service.rb3
-rw-r--r--app/services/users/create_service.rb110
-rw-r--r--app/views/admin/dashboard/index.html.haml30
-rw-r--r--app/views/admin/users/_access_levels.html.haml2
-rw-r--r--app/views/award_emoji/_awards_block.html.haml6
-rw-r--r--app/views/ci/status/_graph_badge.html.haml4
-rw-r--r--app/views/dashboard/todos/index.html.haml16
-rw-r--r--app/views/devise/sessions/_new_base.html.haml2
-rw-r--r--app/views/explore/projects/_filter.html.haml21
-rw-r--r--app/views/help/_shortcuts.html.haml6
-rw-r--r--app/views/layouts/_page.html.haml3
-rw-r--r--app/views/layouts/header/_default.html.haml2
-rw-r--r--app/views/profiles/preferences/show.html.haml10
-rw-r--r--app/views/projects/blob/_upload.html.haml9
-rw-r--r--app/views/projects/blob/edit.html.haml2
-rw-r--r--app/views/projects/blob/new.html.haml2
-rw-r--r--app/views/projects/builds/_sidebar.html.haml5
-rw-r--r--app/views/projects/builds/show.html.haml2
-rw-r--r--app/views/projects/commit/_pipelines_list.haml1
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--app/views/projects/environments/metrics.html.haml4
-rw-r--r--app/views/projects/environments/terminal.html.haml2
-rw-r--r--app/views/projects/merge_requests/_new_compare.html.haml2
-rw-r--r--app/views/projects/merge_requests/merge.js.haml3
-rw-r--r--app/views/projects/milestones/index.html.haml1
-rw-r--r--app/views/projects/new.html.haml2
-rw-r--r--app/views/projects/notes/_note.html.haml2
-rw-r--r--app/views/projects/pipelines/_head.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml10
-rw-r--r--app/views/projects/pipelines/index.html.haml60
-rw-r--r--app/views/projects/services/_index.html.haml2
-rw-r--r--app/views/projects/stage/_in_stage_group.html.haml2
-rw-r--r--app/views/projects/wikis/_sidebar.html.haml5
-rw-r--r--app/views/shared/_milestones_filter.html.haml4
-rw-r--r--app/views/shared/_milestones_sort_dropdown.html.haml22
-rw-r--r--app/views/shared/empty_states/icons/_pipelines_empty.svg1
-rw-r--r--app/views/shared/empty_states/icons/_pipelines_failed.svg1
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/views/shared/projects/_list.html.haml2
-rw-r--r--app/views/shared/projects/_project.html.haml60
-rw-r--r--app/views/users/show.html.haml9
-rwxr-xr-xbin/with_env16
-rw-r--r--changelogs/unreleased/1051-api-create-users-without-password.yml4
-rw-r--r--changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml4
-rw-r--r--changelogs/unreleased/1363-redo-mailroom-support.yml4
-rw-r--r--changelogs/unreleased/1381-present-commits-pagination-headers-correctly.yml4
-rw-r--r--changelogs/unreleased/14492-change-fork-endpoint.yml4
-rw-r--r--changelogs/unreleased/14748-runner-version-in-admin-views.yml4
-rw-r--r--changelogs/unreleased/1648-remove-remnants-of-git-annex-from-ce.yml4
-rw-r--r--changelogs/unreleased/18962-update-issues-button-jumps.yml4
-rw-r--r--changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml4
-rw-r--r--changelogs/unreleased/1937-https-clone-url-username.yml4
-rw-r--r--changelogs/unreleased/19497-hide-relevant-info-when-project-issues-are-disabled.yml4
-rw-r--r--changelogs/unreleased/20495-plus-icon-button.yml4
-rw-r--r--changelogs/unreleased/20732_member_exists_409.yml4
-rw-r--r--changelogs/unreleased/21240_snippets_line_ending.yml4
-rw-r--r--changelogs/unreleased/21605-allow-html5-details.yml4
-rw-r--r--changelogs/unreleased/22018-api-milestone-merge-requests.yml4
-rw-r--r--changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml4
-rw-r--r--changelogs/unreleased/22466-task-list-alignment.yml4
-rw-r--r--changelogs/unreleased/22562-todos-filters.yml4
-rw-r--r--changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml4
-rw-r--r--changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml4
-rw-r--r--changelogs/unreleased/22850-404-when-requesting-build-trace.yml4
-rw-r--r--changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml4
-rw-r--r--changelogs/unreleased/23061-consolidate-project-lists.yml4
-rw-r--r--changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml4
-rw-r--r--changelogs/unreleased/23104-remove-public-param-for-projects.yml4
-rw-r--r--changelogs/unreleased/23535-folders-in-wiki-repository.yml4
-rw-r--r--changelogs/unreleased/23819-fix-milestone-counters-to-top-right-of-panel-headings.yml4
-rw-r--r--changelogs/unreleased/23862-fix-group-project-count.yml4
-rw-r--r--changelogs/unreleased/23948-assign-to-me.yml4
-rw-r--r--changelogs/unreleased/23993-drop-ci_projects.yml6
-rw-r--r--changelogs/unreleased/24215-closed-issues-board.yml4
-rw-r--r--changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml5
-rw-r--r--changelogs/unreleased/24683-sidebar-spinners.yml4
-rw-r--r--changelogs/unreleased/24976-start-of-line-mention.yml4
-rw-r--r--changelogs/unreleased/24998-fix-typo-gitlab-config-file.yml4
-rw-r--r--changelogs/unreleased/25188-polyfill-es-symbol.yml4
-rw-r--r--changelogs/unreleased/25367-add-impersonation-token.yml4
-rw-r--r--changelogs/unreleased/25437-just-emoji.yml4
-rw-r--r--changelogs/unreleased/25465-todo-done-clicking-is-kind-of-unsafe.yml4
-rw-r--r--changelogs/unreleased/25503_issues_finder_performance.yml4
-rw-r--r--changelogs/unreleased/25709-diff-file-overflow.yml4
-rw-r--r--changelogs/unreleased/25920-create-issue-from-failing-build.yml4
-rw-r--r--changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml4
-rw-r--r--changelogs/unreleased/26136-list-repository-tree-api-doc.yml4
-rw-r--r--changelogs/unreleased/26206-fix-download-dropdown.yml4
-rw-r--r--changelogs/unreleased/26286-most-recent-activity-profile-header.yml4
-rw-r--r--changelogs/unreleased/26287-link-branch-in-calendar-activity.yml4
-rw-r--r--changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml4
-rw-r--r--changelogs/unreleased/26315-unify-labels-filter-behavior.yml4
-rw-r--r--changelogs/unreleased/26348-cleanup-navigation-order-groups.yml4
-rw-r--r--changelogs/unreleased/26348-cleanup-navigation-order.yml4
-rw-r--r--changelogs/unreleased/26371-native-emojis-v3-code.yml4
-rw-r--r--changelogs/unreleased/26379-iid-param.yml4
-rw-r--r--changelogs/unreleased/26500-informative-slack-notifications.yml4
-rw-r--r--changelogs/unreleased/26651-cannot-move-project-into-group.yml4
-rw-r--r--changelogs/unreleased/26703-todos-count.yml4
-rw-r--r--changelogs/unreleased/26705-filter-todos-by-manual-add.yml4
-rw-r--r--changelogs/unreleased/26732-combine-deploy-keys-and-push-rules-and-mirror-repository-and-protect-branches-settings-pages.yml5
-rw-r--r--changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml3
-rw-r--r--changelogs/unreleased/26790-label-color-todos.yml4
-rw-r--r--changelogs/unreleased/26847-api-pipelines-use-basic.yml4
-rw-r--r--changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml4
-rw-r--r--changelogs/unreleased/26900-pipelines-tabs.yml4
-rw-r--r--changelogs/unreleased/26957-tanuki-anim-hang.yml4
-rw-r--r--changelogs/unreleased/27032-add-a-house-keeping-api-call.yml4
-rw-r--r--changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml4
-rw-r--r--changelogs/unreleased/27287-label-dropdown-error-messages.yml4
-rw-r--r--changelogs/unreleased/27336-add-environment-url-link-to-terminal-page.yml4
-rw-r--r--changelogs/unreleased/27354-navigation-new-button.yml4
-rw-r--r--changelogs/unreleased/27376-cache-default-branch-pipeline-on-project.yml4
-rw-r--r--changelogs/unreleased/27452-update-issue-count.yml4
-rw-r--r--changelogs/unreleased/27501-api-use-visibility-everywhere.yml4
-rw-r--r--changelogs/unreleased/27503-feature-status-aria-labels.yml4
-rw-r--r--changelogs/unreleased/27520-option-to-prevent-signing-in-from-multiple-ips.yml4
-rw-r--r--changelogs/unreleased/27523-make-stuck-build-detection-more-performant.yml4
-rw-r--r--changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml4
-rw-r--r--changelogs/unreleased/27532_api_changes.yml4
-rw-r--r--changelogs/unreleased/27568-refactor-very-slow-dropdown-asignee-spec.yml4
-rw-r--r--changelogs/unreleased/27574-pipelines-empty-state.yml4
-rw-r--r--changelogs/unreleased/27608-fixes-markdown-in-activity-feed-is-light-gray.yml4
-rw-r--r--changelogs/unreleased/27610-issue-number-alignment.yml4
-rw-r--r--changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml4
-rw-r--r--changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml4
-rw-r--r--changelogs/unreleased/27762-add-default-artifacts-expiration.yml4
-rw-r--r--changelogs/unreleased/27778-a11y-sidebar.yml5
-rw-r--r--changelogs/unreleased/27783-fix-fe-doc-broken-link.yml4
-rw-r--r--changelogs/unreleased/27840-improve-search-bar-experience.yml4
-rw-r--r--changelogs/unreleased/27878-new-service-for-creating-user.yml4
-rw-r--r--changelogs/unreleased/27910-admin-can-create-project-in-all-groups.yml4
-rw-r--r--changelogs/unreleased/27920-both-wip-messages-showing.yml4
-rw-r--r--changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml4
-rw-r--r--changelogs/unreleased/27934-left-align-logo.yml4
-rw-r--r--changelogs/unreleased/27936-make-all-uploads-require-revalidation-on-each-browser-fetch.yml4
-rw-r--r--changelogs/unreleased/27978-improve-task-list-ux.yml4
-rw-r--r--changelogs/unreleased/27994-fix-mr-widget-jump.yml4
-rw-r--r--changelogs/unreleased/28010-mr-merge-button-default-to-danger.yml4
-rw-r--r--changelogs/unreleased/28019-make-builds-show-faster.yml4
-rw-r--r--changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml4
-rw-r--r--changelogs/unreleased/28082-deleted-branch-event-404.yml4
-rw-r--r--changelogs/unreleased/28142-overlap-bugs.yml4
-rw-r--r--changelogs/unreleased/28176_merge_widget_fix.yml4
-rw-r--r--changelogs/unreleased/28186-long-group-names-overflow-out-of-todos-view.yml4
-rw-r--r--changelogs/unreleased/28204-option-to-disable-webpack-dev-server-livereload.yml4
-rw-r--r--changelogs/unreleased/28229-pipelines-loading-icon.yml5
-rw-r--r--changelogs/unreleased/28236-browse-button-dropping.yml4
-rw-r--r--changelogs/unreleased/28247-timeloops-bug.yml4
-rw-r--r--changelogs/unreleased/28253-fix-buid-scroll-button-position.yml4
-rw-r--r--changelogs/unreleased/28257-issues-iids.yml4
-rw-r--r--changelogs/unreleased/28262-horizontal-scrolling-issue-on-long-project-names.yml4
-rw-r--r--changelogs/unreleased/28277-document-u2f-limitations-with-multiple-urls.yml4
-rw-r--r--changelogs/unreleased/28303-change-development-tanuki-favicon-colors-to-match-logo.yml4
-rw-r--r--changelogs/unreleased/28329-allow-slash-in-slash-command-args.yml4
-rw-r--r--changelogs/unreleased/28353-little-grammar-issue.yml4
-rw-r--r--changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml4
-rw-r--r--changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml4
-rw-r--r--changelogs/unreleased/28389-ux-problem-with-pipeline-coverage-placeholder.yml4
-rw-r--r--changelogs/unreleased/28410-dropdown-styling.yml4
-rw-r--r--changelogs/unreleased/28447-hybrid-repository-storages.yml4
-rw-r--r--changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml4
-rw-r--r--changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml4
-rw-r--r--changelogs/unreleased/28462-fix-delimiter-removes-issue-in-todo-counter.yml4
-rw-r--r--changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml4
-rw-r--r--changelogs/unreleased/28516-default-kubernetes-namespace.yml4
-rw-r--r--changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml4
-rw-r--r--changelogs/unreleased/28538-restore-nav-shortcuts.yml4
-rw-r--r--changelogs/unreleased/28598-narrow-environment-payload-by-using-basic-project.yml4
-rw-r--r--changelogs/unreleased/28614-harmonious-color-palette.yml4
-rw-r--r--changelogs/unreleased/28634-todos-margin.yml4
-rw-r--r--changelogs/unreleased/28655-current-path-text-is-not-updated-after-setting-the-new-username.yml4
-rw-r--r--changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml4
-rw-r--r--changelogs/unreleased/28704-fullscreen-zen-mode-is-broken.yml4
-rw-r--r--changelogs/unreleased/28713-fe-style-guide.yml4
-rw-r--r--changelogs/unreleased/28723-consistent-handling-indexof.yml4
-rw-r--r--changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml4
-rw-r--r--changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml4
-rw-r--r--changelogs/unreleased/28835-jobs-head.yml4
-rw-r--r--changelogs/unreleased/28837-remove-help-duplicate.yml4
-rw-r--r--changelogs/unreleased/28865-filter-by-authorized-projects-in-v4.yml4
-rw-r--r--changelogs/unreleased/28890-allow-creating-mr-without-target-branch-in-url.yml5
-rw-r--r--changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml4
-rw-r--r--changelogs/unreleased/28898-fix-search-branches-in-cherry-picking.yml4
-rw-r--r--changelogs/unreleased/28935-make-logo-smaller.yml4
-rw-r--r--changelogs/unreleased/29043-upgrade-vue-and-remove-warnings.yml4
-rw-r--r--changelogs/unreleased/29116-maxint-error.yml4
-rw-r--r--changelogs/unreleased/29263-merge-button-color.yml4
-rw-r--r--changelogs/unreleased/29428-new-directory-from-existing-branch.yml4
-rw-r--r--changelogs/unreleased/29483-spam-check-only-title-and-description.yml4
-rw-r--r--changelogs/unreleased/29550-fix-quick-submit-on-preview.yml4
-rw-r--r--changelogs/unreleased/29555-align-all-todo.yml4
-rw-r--r--changelogs/unreleased/29565-name-of-the-uncompressed-folder-of-a-tag-archive-changed.yml4
-rw-r--r--changelogs/unreleased/29575-polling.yml4
-rw-r--r--changelogs/unreleased/29604-v3-fix-branch-creation.yml4
-rw-r--r--changelogs/unreleased/29897-remove-force-scroll-for-mr-changes-diff.yml4
-rw-r--r--changelogs/unreleased/29930-fix-profile-cover-button-a11y.yml4
-rw-r--r--changelogs/unreleased/30035-milestone-with-due-date-shows-escaped-html.yml4
-rw-r--r--changelogs/unreleased/3440-remove-hsts-header.yml4
-rw-r--r--changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml4
-rw-r--r--changelogs/unreleased/4195-add-sorting-to-project-milestones.yml4
-rw-r--r--changelogs/unreleased/6073_project_api.yml4
-rw-r--r--changelogs/unreleased/9381-authentiq-backchannel-logout.yml4
-rw-r--r--changelogs/unreleased/add-auto-submited-header.yml4
-rw-r--r--changelogs/unreleased/add-changelog-filtered-search-visual-tokens.yml4
-rw-r--r--changelogs/unreleased/add-filtered-search-to-mr.yml4
-rw-r--r--changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml4
-rw-r--r--changelogs/unreleased/add-git-version-to-system-info.yml4
-rw-r--r--changelogs/unreleased/add-issue-modal-loading-indicator.yml4
-rw-r--r--changelogs/unreleased/add-kube-ca-pem-file-deprecate-kube-ca-pem.yml4
-rw-r--r--changelogs/unreleased/add-pipeline-triggers.yml4
-rw-r--r--changelogs/unreleased/add-test-backoff-util.yml4
-rw-r--r--changelogs/unreleased/add-todos-shortcut.yml4
-rw-r--r--changelogs/unreleased/add-yarn-documentation.yml4
-rw-r--r--changelogs/unreleased/add_mr_info_to_issues_list.yml4
-rw-r--r--changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml4
-rw-r--r--changelogs/unreleased/api-drop-subscribed.yml5
-rw-r--r--changelogs/unreleased/api-empty-return.yml4
-rw-r--r--changelogs/unreleased/api-entities.yml4
-rw-r--r--changelogs/unreleased/api-notes-entity-fields.yml4
-rw-r--r--changelogs/unreleased/api-post-block.yml4
-rw-r--r--changelogs/unreleased/api-project-issues-404.yml4
-rw-r--r--changelogs/unreleased/api-remove-deploy-key-disable.yml4
-rw-r--r--changelogs/unreleased/api-remove-owned-groups.yml4
-rw-r--r--changelogs/unreleased/api-star-restful.yml4
-rw-r--r--changelogs/unreleased/api-subscription-restful.yml4
-rw-r--r--changelogs/unreleased/api-todos-restful.yml4
-rw-r--r--changelogs/unreleased/artifactsdoc.yml4
-rw-r--r--changelogs/unreleased/backup_storage_class.yml4
-rw-r--r--changelogs/unreleased/beautiful-karma-output.yml4
-rw-r--r--changelogs/unreleased/branch_deletion.yml4
-rw-r--r--changelogs/unreleased/bypass-email-domain-validation-when-created-by-admin.yml4
-rw-r--r--changelogs/unreleased/cleaner-additional-award-emoji-button.yml4
-rw-r--r--changelogs/unreleased/clear-connections-before-starting-sidekiq.yml4
-rw-r--r--changelogs/unreleased/commons-chunk-plugin.yml5
-rw-r--r--changelogs/unreleased/copy-branch-to-clipboard.yml4
-rw-r--r--changelogs/unreleased/cover-my-karma.yml4
-rw-r--r--changelogs/unreleased/create_branch_repo_less.yml4
-rw-r--r--changelogs/unreleased/dashboard-filter-search-keep-params.yml4
-rw-r--r--changelogs/unreleased/delete-artifacts-for-pages.yml4
-rw-r--r--changelogs/unreleased/diff-make-obvious-cant-comment.yml4
-rw-r--r--changelogs/unreleased/dm-group-reference-full-name.yml4
-rw-r--r--changelogs/unreleased/dynamic-header-fixture.yml4
-rw-r--r--changelogs/unreleased/dynamic-project-title-fixture.yml4
-rw-r--r--changelogs/unreleased/dz-blacklist--names.yml4
-rw-r--r--changelogs/unreleased/dz-change-project-view.yml4
-rw-r--r--changelogs/unreleased/dz-create-nested-groups-via-ui.yml4
-rw-r--r--changelogs/unreleased/dz-dashboard-groups-search.yml4
-rw-r--r--changelogs/unreleased/dz-nested-groups-api.yml4
-rw-r--r--changelogs/unreleased/dz-nested-groups-members.yml4
-rw-r--r--changelogs/unreleased/dz-nested-groups-restrictions.yml4
-rw-r--r--changelogs/unreleased/dz-refactor-full-path.yml4
-rw-r--r--changelogs/unreleased/etag-notes-polling.yml4
-rw-r--r--changelogs/unreleased/expose-pagination-headers.yml4
-rw-r--r--changelogs/unreleased/fe-paginated-environments-api-add-subview.yml4
-rw-r--r--changelogs/unreleased/feature-brand-logo-in-emails.yml4
-rw-r--r--changelogs/unreleased/feature-github-find-users-by-email.yml4
-rw-r--r--changelogs/unreleased/feature-openid-connect.yml4
-rw-r--r--changelogs/unreleased/feature-runner-jobs-v4-api.yml4
-rw-r--r--changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml4
-rw-r--r--changelogs/unreleased/feature-syshook_commits.yml4
-rw-r--r--changelogs/unreleased/feature-tokens-rake-task.yml4
-rw-r--r--changelogs/unreleased/filter-bar-fix-ie.yml4
-rw-r--r--changelogs/unreleased/fix-ci-api-regression-for-after-script.yml4
-rw-r--r--changelogs/unreleased/fix-cycle-analytics-events-limit.yml4
-rw-r--r--changelogs/unreleased/fix-gb-deprecate-ci-config-types.yml4
-rw-r--r--changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml4
-rw-r--r--changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml4
-rw-r--r--changelogs/unreleased/fix-gb-pipeline-retry-builds-started.yml4
-rw-r--r--changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml4
-rw-r--r--changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml4
-rw-r--r--changelogs/unreleased/fix-gb-update-commit-status-api.yml4
-rw-r--r--changelogs/unreleased/fix-issue-23237.yml4
-rw-r--r--changelogs/unreleased/fix-mentioned-issues-for-external-trackers.yml4
-rw-r--r--changelogs/unreleased/fix-prometheus-including-d3-main-bundle.yml4
-rw-r--r--changelogs/unreleased/fix-slow-queries-for-branches-index.yml4
-rw-r--r--changelogs/unreleased/fix_issue_from_milestone.yml4
-rw-r--r--changelogs/unreleased/fixes-namespace-api-documentation.yml4
-rw-r--r--changelogs/unreleased/format-timeago-date.yml4
-rw-r--r--changelogs/unreleased/gfm-autocomplete-fixes.yml4
-rw-r--r--changelogs/unreleased/gitaly-post-receive.yml4
-rw-r--r--changelogs/unreleased/group-memebrs-owner-level.yml4
-rw-r--r--changelogs/unreleased/instrument-in-karma.yml4
-rw-r--r--changelogs/unreleased/introduce-pipeline-triggers.yml4
-rw-r--r--changelogs/unreleased/issue-boards-cant-drag-fix.yml4
-rw-r--r--changelogs/unreleased/issue-descrpiption-spinner-off.yml4
-rw-r--r--changelogs/unreleased/issue-newproj-layout.yml4
-rw-r--r--changelogs/unreleased/issue-tags-layout.yml4
-rw-r--r--changelogs/unreleased/issue_16834.yml4
-rw-r--r--changelogs/unreleased/issue_24815.yml4
-rw-r--r--changelogs/unreleased/issue_25900.yml4
-rw-r--r--changelogs/unreleased/issue_26701.yml4
-rw-r--r--changelogs/unreleased/list_issues_with_no_labels.yml4
-rw-r--r--changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml4
-rw-r--r--changelogs/unreleased/long-file-name-overflow.yml4
-rw-r--r--changelogs/unreleased/migrate-pipeline-events-and-email-service.yml4
-rw-r--r--changelogs/unreleased/mock-ci-service.yml4
-rw-r--r--changelogs/unreleased/move_tags_service_to_namespace.yml4
-rw-r--r--changelogs/unreleased/moving-issue-with-two-list-labels.yml4
-rw-r--r--changelogs/unreleased/mr-diff-comment-button.yml4
-rw-r--r--changelogs/unreleased/new-branch-fixture.yml4
-rw-r--r--changelogs/unreleased/only-create-unmergeable-todo-once.yml4
-rw-r--r--changelogs/unreleased/only-yield-valid-reference-matches.yml4
-rw-r--r--changelogs/unreleased/pages-0-4-0.yml4
-rw-r--r--changelogs/unreleased/paginate-all-the-things.yml4
-rw-r--r--changelogs/unreleased/pass in current_user in MergeRequest and MergeRequestsHelper.yml4
-rw-r--r--changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml4
-rw-r--r--changelogs/unreleased/pipeline-blocking-actions.yml4
-rw-r--r--changelogs/unreleased/pipelines-build-tooltip.yml4
-rw-r--r--changelogs/unreleased/priority-to-label-priority.yml4
-rw-r--r--changelogs/unreleased/projects-list-line-breaks.yml4
-rw-r--r--changelogs/unreleased/protected-branch-dropdown-titles.yml4
-rw-r--r--changelogs/unreleased/quick-submit-fixture.yml4
-rw-r--r--changelogs/unreleased/remember-me-missasligned-mobile.yml4
-rw-r--r--changelogs/unreleased/removal_of_unused_parameter.yml4
-rw-r--r--changelogs/unreleased/remove-es6-extension.yml4
-rw-r--r--changelogs/unreleased/remove-inactive-default-email-services.yml4
-rw-r--r--changelogs/unreleased/remove-jquery-ui-datepicker.yml4
-rw-r--r--changelogs/unreleased/remove-jquery-ui-plugins.yml4
-rw-r--r--changelogs/unreleased/remove-jquery-ui-sortable.yml4
-rw-r--r--changelogs/unreleased/remove-new-relic-gem.yml4
-rw-r--r--changelogs/unreleased/remove-readme-option.yml4
-rw-r--r--changelogs/unreleased/remove-subscribe-label-tooltip.yml4
-rw-r--r--changelogs/unreleased/remove-unused-ci-tables.yml4
-rw-r--r--changelogs/unreleased/rename-ci_commits-to-ci_pipeline.yml4
-rw-r--r--changelogs/unreleased/rename-retry-failed-pipeline-to-retry.yml4
-rw-r--r--changelogs/unreleased/rename_delete_services.yml4
-rw-r--r--changelogs/unreleased/rename_files_delete_service.yml4
-rw-r--r--changelogs/unreleased/replace-npm-with-yarn.yml4
-rw-r--r--changelogs/unreleased/replace_closing_mr_icon.yml4
-rw-r--r--changelogs/unreleased/requires-input-fixture.yml4
-rw-r--r--changelogs/unreleased/rfr-20170307-change-default-project-number-limit.yml4
-rw-r--r--changelogs/unreleased/routes-lower-case.yml4
-rw-r--r--changelogs/unreleased/rss-btn-alignment-fix.yml4
-rw-r--r--changelogs/unreleased/seed-abuse-reports.yml4
-rw-r--r--changelogs/unreleased/set-default-cache-key-for-jobs.yml4
-rw-r--r--changelogs/unreleased/settings-tab.yml4
-rw-r--r--changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml4
-rw-r--r--changelogs/unreleased/sh-delete-user-permission-check.yml4
-rw-r--r--changelogs/unreleased/sh-remove-tags-from-explore.yml4
-rw-r--r--changelogs/unreleased/slow-search-changelog.yml4
-rw-r--r--changelogs/unreleased/snippets-search.yml4
-rw-r--r--changelogs/unreleased/sort-builds-in-stage-dropdown.yml4
-rw-r--r--changelogs/unreleased/ssh-key-paste.yml4
-rw-r--r--changelogs/unreleased/ssrf-protections.yml4
-rw-r--r--changelogs/unreleased/static-navbar.yml4
-rw-r--r--changelogs/unreleased/task_list_refactor.yml4
-rw-r--r--changelogs/unreleased/tc-api-pipeline-jobs.yml4
-rw-r--r--changelogs/unreleased/tc-fix-project-create-500.yml4
-rw-r--r--changelogs/unreleased/tc-pipeline-show-trigger-date.yml4
-rw-r--r--changelogs/unreleased/time-tracking-color-not-consistent.yml4
-rw-r--r--changelogs/unreleased/unified-member-api-response.yml4
-rw-r--r--changelogs/unreleased/update-ace.yml4
-rw-r--r--changelogs/unreleased/update-vue-2-1.yml4
-rw-r--r--changelogs/unreleased/use-redis-channel-to-post-runner-notifcations.yml4
-rw-r--r--changelogs/unreleased/user-calendar-border.yml4
-rw-r--r--changelogs/unreleased/user-callout-showing-on-all-profiles.yml4
-rw-r--r--changelogs/unreleased/user-callouts.yml4
-rw-r--r--changelogs/unreleased/user-profile-join-date.yml4
-rw-r--r--changelogs/unreleased/workhorse-1-4-0.yml4
-rw-r--r--changelogs/unreleased/zj-builds-to-jobs-api.yml4
-rw-r--r--changelogs/unreleased/zj-chat-notification-default-branch.yml4
-rw-r--r--changelogs/unreleased/zj-variables-build-job.yml4
-rw-r--r--config/environments/test.rb3
-rw-r--r--config/gitlab.yml.example2
-rw-r--r--config/webpack.config.js6
-rw-r--r--db/migrate/20170317203554_index_routes_path_for_like.rb29
-rw-r--r--db/schema.rb7
-rw-r--r--doc/administration/gitaly/index.md86
-rw-r--r--doc/administration/high_availability/database.md2
-rw-r--r--doc/administration/high_availability/nfs.md2
-rw-r--r--doc/api/README.md135
-rw-r--r--doc/api/pipeline_triggers.md10
-rw-r--r--doc/api/users.md121
-rw-r--r--doc/api/v3_to_v4.md1
-rw-r--r--doc/development/README.md2
-rw-r--r--doc/development/ci_setup.md2
-rw-r--r--doc/development/fe_guide/accessibility.md13
-rw-r--r--doc/development/fe_guide/architecture.md22
-rw-r--r--doc/development/fe_guide/design_patterns.md78
-rw-r--r--doc/development/fe_guide/index.md92
-rw-r--r--doc/development/fe_guide/performance.md95
-rw-r--r--doc/development/fe_guide/security.md92
-rw-r--r--doc/development/fe_guide/style_guide_js.md408
-rw-r--r--doc/development/fe_guide/style_guide_scss.md (renamed from doc/development/scss_styleguide.md)56
-rw-r--r--doc/development/fe_guide/testing.md129
-rw-r--r--doc/development/fe_guide/vue.md104
-rw-r--r--doc/development/frontend.md511
-rw-r--r--doc/development/rake_tasks.md14
-rw-r--r--doc/development/testing.md11
-rw-r--r--doc/install/installation.md33
-rw-r--r--doc/profile/README.md6
-rw-r--r--doc/profile/preferences.md37
-rw-r--r--doc/update/8.13-to-8.14.md2
-rw-r--r--doc/update/8.17-to-9.0.md87
-rw-r--r--doc/user/profile/preferences.md64
-rw-r--r--doc/workflow/shortcuts.md1
-rw-r--r--features/steps/project/issues/award_emoji.rb2
-rw-r--r--features/steps/shared/diff_note.rb4
-rw-r--r--features/steps/shared/markdown.rb2
-rw-r--r--features/steps/shared/note.rb2
-rw-r--r--lib/api/users.rb27
-rw-r--r--lib/api/v3/users.rb53
-rw-r--r--lib/gitlab/ci/build/step.rb9
-rw-r--r--lib/gitlab/ee_compat_check.rb295
-rw-r--r--lib/gitlab/etag_caching/store.rb2
-rw-r--r--lib/gitlab/gitaly_client.rb7
-rw-r--r--lib/gitlab/o_auth/user.rb6
-rw-r--r--lib/gitlab/testing/request_blocker_middleware.rb61
-rwxr-xr-xlib/support/init.d/gitlab67
-rw-r--r--lib/support/init.d/gitlab.default.example4
-rw-r--r--lib/tasks/gitlab/assets.rake8
-rw-r--r--lib/tasks/gitlab/gitaly.rake23
-rw-r--r--lib/tasks/gitlab/task_helpers.rb2
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake2
-rw-r--r--lib/tasks/tokens.rake38
-rw-r--r--package.json3
-rw-r--r--spec/controllers/import/bitbucket_controller_spec.rb11
-rw-r--r--spec/controllers/projects/builds_controller_specs.rb47
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb24
-rw-r--r--spec/controllers/registrations_controller_spec.rb9
-rw-r--r--spec/factories/ci/builds.rb2
-rw-r--r--spec/factories/merge_requests.rb1
-rw-r--r--spec/features/admin/admin_broadcast_messages_spec.rb2
-rw-r--r--spec/features/admin/admin_settings_spec.rb1
-rw-r--r--spec/features/boards/add_issues_modal_spec.rb14
-rw-r--r--spec/features/commits_spec.rb13
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb5
-rw-r--r--spec/features/explore/groups_list_spec.rb20
-rw-r--r--spec/features/merge_requests/create_new_mr_spec.rb33
-rw-r--r--spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb2
-rw-r--r--spec/features/participants_autocomplete_spec.rb95
-rw-r--r--spec/features/projects/blobs/user_create_spec.rb18
-rw-r--r--spec/features/projects/compare_spec.rb3
-rw-r--r--spec/features/projects/deploy_keys_spec.rb26
-rw-r--r--spec/features/projects/group_links_spec.rb24
-rw-r--r--spec/features/projects/members/user_requests_access_spec.rb2
-rw-r--r--spec/features/projects/milestones/milestones_sorting_spec.rb52
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb2
-rw-r--r--spec/features/projects/user_create_dir_spec.rb72
-rw-r--r--spec/features/user_callout_spec.rb8
-rw-r--r--spec/features/users/projects_spec.rb31
-rw-r--r--spec/helpers/avatars_helper_spec.rb21
-rw-r--r--spec/helpers/namespaces_helper_spec.rb33
-rw-r--r--spec/helpers/todos_helper_spec.rb34
-rw-r--r--spec/helpers/users_helper_spec.rb17
-rw-r--r--spec/javascripts/boards/board_card_spec.js3
-rw-r--r--spec/javascripts/boards/boards_store_spec.js5
-rw-r--r--spec/javascripts/boards/issue_card_spec.js3
-rw-r--r--spec/javascripts/boards/list_spec.js3
-rw-r--r--spec/javascripts/boards/modal_store_spec.js1
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js7
-rw-r--r--spec/javascripts/cycle_analytics/limit_warning_component_spec.js39
-rw-r--r--spec/javascripts/filtered_search/filtered_search_manager_spec.js2
-rw-r--r--spec/javascripts/fixtures/pipelines.html.haml14
-rw-r--r--spec/javascripts/fixtures/pipelines_table.html.haml3
-rw-r--r--spec/javascripts/issuable_time_tracker_spec.js6
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js67
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js163
-rw-r--r--spec/javascripts/test_bundle.js6
-rw-r--r--spec/javascripts/user_callout_spec.js18
-rw-r--r--spec/javascripts/vue_pipelines_index/empty_state_spec.js38
-rw-r--r--spec/javascripts/vue_pipelines_index/error_state_spec.js23
-rw-r--r--spec/javascripts/vue_pipelines_index/mock_data.js107
-rw-r--r--spec/javascripts/vue_pipelines_index/nav_controls_spec.js93
-rw-r--r--spec/javascripts/vue_pipelines_index/pipelines_spec.js114
-rw-r--r--spec/lib/gitlab/ci/build/step_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_snippet_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb8
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb28
-rw-r--r--spec/lib/gitlab/git/compare_spec.rb10
-rw-r--r--spec/lib/gitlab/git/diff_collection_spec.rb4
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb10
-rw-r--r--spec/lib/gitlab/git/util_spec.rb2
-rw-r--r--spec/lib/gitlab/ldap/user_spec.rb2
-rw-r--r--spec/mailers/emails/profile_spec.rb10
-rw-r--r--spec/mailers/notify_spec.rb380
-rw-r--r--spec/models/concerns/issuable_spec.rb28
-rw-r--r--spec/models/hooks/system_hook_spec.rb5
-rw-r--r--spec/models/issue_spec.rb37
-rw-r--r--spec/models/milestone_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb6
-rw-r--r--spec/models/pages_domain_spec.rb14
-rw-r--r--spec/models/project_spec.rb7
-rw-r--r--spec/models/repository_spec.rb2
-rw-r--r--spec/models/route_spec.rb20
-rw-r--r--spec/models/snippet_spec.rb43
-rw-r--r--spec/models/user_spec.rb13
-rw-r--r--spec/requests/api/projects_spec.rb2
-rw-r--r--spec/requests/api/v3/projects_spec.rb2
-rw-r--r--spec/requests/api/v3/users_spec.rb14
-rw-r--r--spec/services/boards/issues/list_service_spec.rb3
-rw-r--r--spec/services/create_branch_service_spec.rb24
-rw-r--r--spec/services/issuable/bulk_update_service_spec.rb2
-rw-r--r--spec/services/milestones/close_service_spec.rb2
-rw-r--r--spec/services/projects/fork_service_spec.rb4
-rw-r--r--spec/services/spam_service_spec.rb71
-rw-r--r--spec/services/users/create_service_spec.rb182
-rw-r--r--spec/spec_helper.rb3
-rw-r--r--spec/support/notify_shared_examples.rb140
-rw-r--r--spec/support/prometheus_helpers.rb4
-rw-r--r--spec/support/slack_mattermost_notifications_shared_examples.rb19
-rw-r--r--spec/support/target_branch_helpers.rb16
-rw-r--r--spec/support/test_env.rb5
-rw-r--r--spec/support/wait_for_requests.rb32
-rw-r--r--spec/support/wait_for_vue_resource.rb2
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb78
-rw-r--r--spec/tasks/gitlab/workhorse_rake_spec.rb3
-rw-r--r--spec/tasks/tokens_spec.rb21
-rw-r--r--spec/views/projects/pipelines/show.html.haml_spec.rb10
-rw-r--r--tmp/sockets/private/.gitkeep0
-rw-r--r--vendor/assets/javascripts/js.cookie.js156
-rw-r--r--yarn.lock10
707 files changed, 6494 insertions, 4006 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 34c10b3b77f..f271ab4c4c8 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -19,7 +19,7 @@ before_script:
- source ./scripts/prepare_build.sh
- cp config/gitlab.yml.example config/gitlab.yml
- bundle --version
- - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) $FLAGS'
+ - '[ "$USE_BUNDLE_INSTALL" != "true" ] || retry bundle install --without postgres production --jobs $(nproc) --clean $FLAGS'
- retry gem install knapsack
- '[ "$SETUP_DB" != "true" ] || bundle exec rake db:drop db:create db:schema:load db:migrate add_limits_mysql'
@@ -206,10 +206,9 @@ rake ee_compat_check:
- /^[\d-]+-stable(-ee)?$/
allow_failure: yes
cache:
- key: "ruby233-ee_compat_check_repo"
+ key: "ee_compat_check_repo"
paths:
- - ee_compat_check/repo/
- - vendor/ruby
+ - ee_compat_check/ee-repo/
artifacts:
name: "${CI_JOB_NAME}_${CI_COMIT_REF_NAME}_${CI_COMMIT_SHA}"
when: on_failure
@@ -281,6 +280,7 @@ rake karma:
BABEL_ENV: "coverage"
script:
- bundle exec rake karma
+ coverage: '/^Statements *: (\d+\.\d+%)/'
artifacts:
name: coverage-javascript
expire_in: 31d
@@ -312,7 +312,7 @@ bundler:audit:
- master@gitlab/gitlabhq
- master@gitlab/gitlab-ee
script:
- - "bundle exec bundle-audit check --update"
+ - "bundle exec bundle-audit check --update --ignore CVE-2016-4658"
migration paths:
stage: test
@@ -333,6 +333,7 @@ migration paths:
- bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- bundle exec rake db:drop db:create db:schema:load db:seed_fu
- git checkout $CI_COMMIT_SHA
+ - bundle install --without postgres production --jobs $(nproc) $FLAGS --retry=3
- source scripts/prepare_build.sh
- bundle exec rake db:migrate
@@ -345,6 +346,7 @@ coverage:
USE_BUNDLE_INSTALL: "true"
script:
- bundle exec scripts/merge-simplecov
+ coverage: '/LOC \((\d+\.\d+%)\) covered.$/'
artifacts:
name: coverage
expire_in: 31d
diff --git a/CHANGELOG.md b/CHANGELOG.md
index da1898e3770..4291eca8dc7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,289 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 9.0.0 (2017-03-22)
+
+- Fix inconsistent naming for services that delete things. !5803 (dixpac)
+- UI: Allow a project variable to be set to an empty value. !6044 (Lukáš Nový)
+- Align task list checkboxes. !6487 (Jared Deckard <jared.deckard@gmail.com>)
+- SanitizationFilter allows html5 details and summary tags. !6568
+- on branch deletion show loading icon and disabled the button. !6761 (wendy0402)
+- Use an entity for RepoBranch commits and enhance RepoCommit. !7138 (Ben Boeckel)
+- Deleting a user doesn't delete issues they've created/are assigned to. !7393
+- Fix position of counter in milestone panels. !7842 (Andrew Smith (EspadaV8))
+- Added a feature to create a 'directly addressed' Todo when mentioned in the beginning of a line. !7926 (Ershad Kunnakkadan)
+- Implement OpenID Connect identity provider. !8018 (Markus Koller)
+- Show directory hierarchy when listing wiki pages. !8133 (Alex Braha Stoll)
+- Migrate SlackService and MattermostService from build_events to pipeline_events, and migrate BuildsEmailService to PipelinesEmailService. Update Hipchat to use pipeline events rather than build events. !8196
+- Execute web hooks for WikiPage delete operation. !8198
+- Added external environment link to web terminal view. !8303
+- Responsive title in diffs inline, side by side, with and without sidebar. !8475
+- Bypass email domain validation when a user is created by an admin. !8575 (Reza Mohammadi @remohammadi)
+- API: Paginate all endpoints that return an array. !8606 (Robert Schilling)
+- pass in current_user in MergeRequest and MergeRequestsHelper. !8624 (Dongqing Hu)
+- Add user & build links in Slack Notifications. !8641 (Poornima M)
+- Todo done clicking is kind of unusable. !8691 (Jacopo Beschi @jacopo-beschi)
+- Filter todos by manual add. !8691 (Jacopo Beschi @jacopo-beschi)
+- Add runner version to /admin/runners view. !8733 (Jonathon Reinhart)
+- API: remove `public` param for projects. !8736
+- Allow creating nested groups via UI. !8786
+- API: Add environment stop action. !8808
+- Add discussion events to contributions calendar. !8821
+- Unify issues search behavior by always filtering when ALL labels matches. !8849
+- V3 deprecated templates endpoints removal. !8853
+- Expose pipelines as PipelineBasic `api/v3/projects/:id/pipelines`. !8875
+- Alphabetically sort tags on runner list. !8922 (blackst0ne)
+- Added documentation for permalinks to most recent build artifacts. !8934 (Christian Godenschwager)
+- Standardize branch name params as branch on V4 API. !8936
+- Move /projects/fork/:id to /projects/:id/fork. !8940
+- Fix small height of activity header page. !8952 (Pavel Sorokin)
+- Optionally make users created via the API set their password. !8957 (Joost Rijneveld)
+- GitHub Importer - Find users based on GitHub email address. !8958
+- API: Consolidate /projects endpoint. !8962
+- Add filtered search visual tokens. !8969
+- Store group and project full name and full path in routes table. !8979
+- Add internal API to notify Gitaly of post receive. !8983
+- Remove inactive default email services. !8987
+- Option to prevent signing in from multiple ips. !8998
+- Download snippets with LF line-endings by default. !8999
+- Fixes dropdown width in admin project page. !9002
+- fixes issue number alignment problem in MR and issue list. !9020
+- Fix CI/CD pipeline retry and take stages order into account. !9021
+- Make stuck builds detection more performant. !9025
+- Filter by projects in the end of search. !9030
+- Add nested groups to the API. !9034
+- Use ETag to improve performance of issue notes polling. !9036
+- Add the oauth2_generic OmniAuth strategy. !9048 (Joe Marty)
+- Brand header logo for pipeline emails. !9049 (Alexis Reigel)
+- replace npm with yarn and add yarn.lock. !9055
+- Fix displaying error messages for create label dropdown. !9058 (Tom Koole)
+- Set dropdown height fixed to 250px and make it scrollable. !9063
+- Update API docs for new namespace format. !9073 (Markus Koller)
+- Replace static fixture for behaviors/quick_submit_spec.js. !9086 (winniehell)
+- Use iids as filter parameter. !9096
+- Manage user personal access tokens through api and add impersonation tokens. !9099 (Simon Vocella)
+- Added the ability to copy a branch name to the clipboard. !9103 (Glenn Sayers)
+- Rename Files::DeleteService to Files::DestroyService. !9110 (dixpac)
+- Fixes FE Doc broken link. !9120
+- Add git version to gitlab:env:info. !9128 (Semyon Pupkov)
+- Replace static fixture for new_branch_spec.js. !9131 (winniehell)
+- Reintroduce coverage report for JavaScript. !9133 (winniehell)
+- Fix MR widget jump. !9146
+- Avoid calling Build#trace_with_state for performance. !9149 (Takuya Noguchi)
+- fix background color for labels mention in todo. !9155 (mhasbini)
+- Replace static fixture for behaviors/requires_input_spec.js. !9162 (winniehell)
+- Added AsciiDoc Snippet to CI/CD Badges. !9164 (Jan Christophersen)
+- Make Karma output look nicer for CI. !9165 (winniehell)
+- show 99+ for large count in todos notification bell. !9171 (mhasbini)
+- Replace static fixture for header_spec.js. !9174 (winniehell)
+- Replace static fixture for project_title_spec.js. !9175 (winniehell)
+- Fixes markdown in activity-feed is gray. !9179
+- Show notifications settings dropdown even if repository feature is disabled. !9180
+- Fixes job dropdown action throws error in js console. !9182
+- Set maximum width for mini pipeline graph text so it is not truncated to early. !9188
+- Added 'Most Recent Activity' header to the User Profile page. !9189 (Jan Christophersen)
+- Show Issues mentioned / being closed from a Merge Requests title below the 'Accept Merge Request' button. !9194 (Jan Christophersen)
+- Stop linking to deleted Branches in Activity tabs. !9203 (Jan Christophersen)
+- Make it possible to pass coverage value to commit status API. !9214 (wendy0402)
+- Add admin setting for default artifacts expiration. !9219
+- add :iids param to IssuableFinder (resolve technical dept). !9222 (mhasbini)
+- Add Links to Branches in Calendar Activity. !9224 (Jan Christophersen)
+- Fix pipeline retry and cancel buttons on pipeline details page. !9225
+- Remove es6 file extension from JavaScript files. !9241 (winniehell)
+- Add Runner's registration/deletion v4 API. !9246
+- Add merge request count to each issue on issues list. !9252 (blackst0ne)
+- Fix error in MR widget after /merge slash command. !9259
+- Clean-up Project navigation order. !9272
+- Add Runner's jobs v4 API. !9273
+- Add pipeline trigger API with user permissions. !9277
+- Enhanced filter issues layout for better mobile experiance. !9280 (Pratik Borsadiya)
+- Move babel config for instanbul to karma config. !9286 (winniehell)
+- Document U2F limitations with multiple URLs. !9300
+- Wrap long Project and Group titles. !9301
+- Clean-up Groups navigation order. !9309
+- Truncate long Todo titles for non-mobile screens. !9311
+- add rake tasks to handle yarn dependencies and update documentation. !9316
+- API: - Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource. !9325 (Robert Schilling)
+- API: Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar`. !9328 (Robert Schilling)
+- API: Use `visibility` as string parameter everywhere. !9337
+- Add the Username to the HTTP(S) clone URL of a Repository. !9347 (Jan Christophersen)
+- Add spec for todo with target_type Commit. !9351 (George Andrinopoulos)
+- API: Remove `DELETE projects/:id/deploy_keys/:key_id/disable`. !9365 (Robert Schilling)
+- Fixes includes line number during unfold copy n paste in parallel diff view. !9365
+- API: Use POST to (un)block a user. !9371 (Robert Schilling)
+- Remove markup that was showing in tooltip for renamed files. !9374
+- Drop unused ci_projects table and some unused project_id columns, then rename gl_project_id to project_id. Stop exporting job trace when exporting projects. !9378 (David Wagner)
+- Adds remote logout functionality to the Authentiq OAuth provider. !9381 (Alexandros Keramidas)
+- Introduce /award slash command; Allow posting of just an emoji in comment. !9382 (mhasbini)
+- API: Remove deprecated fields Notes#upvotes and Notes#downvotes. !9384 (Robert Schilling)
+- Redo internals of Incoming Mail Support. !9385
+- update Vue to v2.1.10. !9386
+- Add button to create issue for failing build. !9391 (Alex Sanford)
+- test compiling production assets and generate webpack bundle report in CI. !9396
+- API: Return 204 for all delete endpoints. !9397 (Robert Schilling)
+- Add KUBE_CA_PEM_FILE, deprecate KUBE_CA_PEM. !9398
+- API: Use POST requests to mark todos as done. !9410 (Robert Schilling)
+- API project create: Make name or path required. !9416
+- Add housekeeping endpoint for Projects API. !9421
+- Fixes delimiter removes when todo marked as done. !9435
+- Document when current coverage configuration option was introduced. !9443
+- Uploaded files which content can change now require revalidation on each page load. !9453
+- Only add a newline in the Markdown Editor if the current line is not empty. !9455 (Jan Christophersen)
+- Rename builds to job for the v4 API. !9463
+- API: Remove /groups/owned endpoint. !9505 (Robert Schilling)
+- API: Return 400 for all validation erros in the mebers API. !9523 (Robert Schilling)
+- Fixes large file name tooltip cutoff in diff header. !9529
+- Keep consistent in handling indexOf results. !9531 (Takuya Noguchi)
+- Make documentation of list repository tree API call more detailed. !9532 (Marius Kleiner)
+- Fix Sort dropdown reflow issue. !9533 (Jarkko Tuunanen)
+- Improve grammar in GitLab flow documentation. !9552 (infogrind)
+- Change default project view for user from readme to files view. !9584
+- Make it possible to configure blocking manual actions. !9585
+- Show public RSS feeds to anonymous users. !9596 (Michael Kozono)
+- Update storage settings to allow extra values per repository storage. !9597
+- Enable filtering milestones by search criteria in the API. !9606
+- Ensure archive download is only one directory deep. !9616
+- Fix updaing commit status when using optional attributes. !9618
+- Add filter and sorting to dashboard groups page. !9619
+- Remove deprecated build status badge and related services. !9620
+- Remove the newrelic gem. !9622 (Robert Schilling)
+- Rename table ci_commits to ci_pipelines. !9638
+- Remove various unused CI tables and columns. !9639
+- Use webpack CommonsChunkPlugin to place common javascript libraries in their own bundles. !9647
+- CORS: Whitelist pagination headers. !9651 (Robert Schilling)
+- Remove "subscribed" field from API responses returning list of issues or merge requests. !9661
+- Highlight line number if specified on diff pages when page loads. !9664
+- Set default cache key to "default" for jobs. !9666
+- Set max height to screen height for Zen mode. !9667
+- GET 'projects/:id/repository/commits' endpoint improvements. !9679 (George Andrinopoulos, Jordan Ryan Reuter)
+- Restore keyboard shortcuts for "Activity" and "Charts". !9680
+- Added commit array to Syshook json. !9685 (Gabriele Pongelli)
+- Document ability to list issues with no labels using API. !9697 (Vignesh Ravichandran)
+- Fix typo in Gitlab config file. !9702 (medied)
+- Fix json response in branches controller. !9710 (George Andrinopoulos)
+- Refactor dropdown_assignee_spec. !9711 (George Andrinopoulos)
+- Delete artifacts for pages unless expiry date is specified. !9716
+- Use gitlab-workhorse 1.4.0. !9724
+- Add GET /projects/:id/pipelines/:pipeline_id/jobs endpoint. !9727
+- Restrict nested group names to prevent ambiguous routes. !9738
+- Rename job environment variables to new terminology. !9756
+- Deprecate usage of `types` configuration entry to describe CI/CD stages. !9766
+- Moved project settings from the gear drop-down menu to a tab. !9786
+- Fix "passed with warnings" stage status on MySQL installations. !9802
+- Fix for creating a project through API when import_url is nil. !9841
+- Use GitLab Pages v0.4.0. !9896
+- Reserve few project and nested group paths that have wildcard routes associated. !9898
+- Speed up project dashboard by caching pipeline status and eager loading routes. !9903
+- Fixes n+1 query for tags and branches index page. !9905
+- Hide ancestor groups in the share group dropdown list. !9965
+- Allow creating merge request even if target branch is not specified in query params. !9968
+- Removed d3 from the main application.js bundle. !10062
+- Return 404 in project issues API endpoint when project cannot be found. !10093
+- Fix positioning of `Scroll to top` button.
+- Add limit to the number of events showed in cycle analytics.
+- Only run timeago loops after rendering timeago components.
+- Increase right side of file header to button stays on same line.
+- Centers loading icon vertically and horizontally in pipelines table in commit view.
+- Fix issues mentioned but not closed for external issue trackers.
+- fix milestone does not automatically assign when create issue from milestone.
+- Re-add Assign to me link to Merge Request and Issues.
+- Format timeago date to short format.
+- Fix errors in slash commands matcher, add simple test coverage. (YarNayar)
+- Make Git history follow renames again by performing the --skip in Ruby.
+- Added option to update to owner for group members.
+- Pick up option from GDK to disable webpack dev server livereload.
+- Introduce Pipeline Triggers that are user-aware.
+- Fixed loading spinner position on issue template toggle.
+- Removed duplicate "Visibility Level" label on New Project page. (Robert Marcano)
+- Fix 'New Tag' layout on Tags page. (Robert Marcano)
+- Update API endpoints for raw files.
+- Fix issuable stale object error handler for js when updating tasklists.
+- Gather issuable metadata to avoid n+1 queries on index view.
+- Remove JIRA closed status icon.
+- Fix z index issues with sidebar.
+- Fixed long file names overflowing under action buttons.
+- Only show public emails in atom feeds.
+- Add Mock CI service/integration for development.
+- Move tag services to Tags namespace. (dixpac)
+- Set Auto-Submitted header to mails. (Semyon Pupkov)
+- Improved diff comment button UX.
+- Adds API endpoint to fetch all merge request for a single milestone. (Joren De Groof)
+- Only create unmergeable todos once when MR fails to merge.
+- Only yield valid references in ReferenceFilter.references_in.
+- Add member: Always return 409 when a member exists.
+- Remove plus icon from MR button on compare view.
+- Re-add the New Project button in nav bar.
+- Default to subtle MR mege button until CI status is available.
+- Rename priority sorting option to label priority.
+- Added headers to protected branch access dropdowns.
+- Hide issue info when project issues are disabled. (George Andrinopoulos)
+- removed unused parameter 'status_only: true'.
+- Left align logo.
+- Replaced jQuery UI datepicker.
+- Removed jQuery UI highlight & autocomplete.
+- Replaced jQuery UI sortable.
+- Remove readme-only project view preference.
+- Remove tooltips from label subscription buttons.
+- Rename retry failed button on pipeline page to just retry.
+- Align bulk update issues button to the right.
+- Remove remnants of git annex support.
+- Dispatch needed JS when creating a new MR in diff view.
+- Change project count limit from 10 to 100000.
+- Remove repeated routes.path check for postgresql database. (mhasbini)
+- Fixed RSS button alignment on activity pages.
+- Seed abuse reports for development.
+- Bump Hashie to 3.5.5 and omniauth to 1.4.2 to eliminate warning noise.
+- Add user deletion permission check in `Users::DestroyService`.
+- Fix snippets search result spacing.
+- Sort builds in stage dropdown.
+- SSH key field updates title after pasting key.
+- To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443.
+- Remove fixed positioning from top nav.
+- Deduplicate markdown task lists.
+- update issue count when closing/reopening an issue.
+- Update code editor (ACE) to 1.2.6, to fix input problems with compose key.
+- Improves a11y in sidebar by adding aria-hidden attributes in i tags and by fixing two broken aria-hidden attributes.
+- Use redis channel to post notifications.
+- Removed top border from user contribution calendar.
+- Added user callouts to the projects dashboard and user profile.
+- Removes label when moving issue to another list that it is currently in.
+- Return 202 with JSON body on async removals on V4 API.
+- Add filtered search to MR page.
+- Add frequently used emojis back to awards menu.
+- don't animate logo when downloading files.
+- Stop setting Strict-Transport-Securty header from within the app.
+- Use "branch_name" instead "branch" on V3 branch creation API.
+- Fix archive prefix bug for refs containing dots.
+- ensure MR widget dropdown is same color as button.
+- Adds Pending and Finished tabs to pipelines page.
+- Decrease tanuki logo size.
+- Add all available statuses to scope filter for project builds endpoint. (George Andrinopoulos)
+- Add filter param for project membership for current_user in API v4.
+- Remove help link from right dropdown.
+- Fix jobs table header height.
+- Combined deploy keys, push rules, protect branches and mirror repository settings options into a single one called Repository.
+- Add storage class configuration option for Amazon S3 remote backups. (Jon Keys)
+- Specify in the documentation that only projects owners can transfer projects.
+- Use native unicode emojis.
+- Clear ActiveRecord connections before starting Sidekiq.
+- Update account view to display new username.
+- Narrow environment payload by using basic project details resource.
+- Creating a new branch from an issue will automatically initialize a repository if one doesn't already exist.
+- Dashboard project search keeps selected sort & filters.
+- Visually show expanded diff lines cant have comments.
+- Use full group name in GFM group reference title.
+- Make a default namespace of Kubernetes service to contain project ID.
+- Present GitLab version for each V3 to V4 API change on v3_to_v4.md.
+- Add badges to global dropdown.
+- Changed coverage reg expression placeholder text to be more like a placeholder.
+- Show members of parent groups on project members page.
+- Fix grammer issue in admin/runners.
+- Allow slashes in slash command arguments.
+- Adds paginationd and folders view to environments table.
+- hide loading spinners for server-rendered sidebar fields.
+- Change development tanuki favicon colors to match logo color order.
+- API issues - support filtering by iids.
+
## 8.17.4 (2017-03-19)
- Only show public emails in atom feeds.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a285e8ab74f..275c0cd1777 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -82,7 +82,7 @@ If a contributor is no longer actively working on a submitted merge request
we can decide that the merge request will be finished by one of our
[Merge request coaches][team] or close the merge request. We make this decision
based on how important the change is for our product vision. If a Merge request
-coach is going to finish the merge request we assign the
+coach is going to finish the merge request we assign the
~"coach will finish" label.
## Helping others
@@ -479,8 +479,7 @@ merge request:
1. [Rails](https://github.com/bbatsov/rails-style-guide)
1. [Newlines styleguide][newlines-styleguide]
1. [Testing](doc/development/testing.md)
-1. [JavaScript (ES6)](https://github.com/airbnb/javascript)
-1. [JavaScript (ES5)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5)
+1. [JavaScript styleguide][js-styleguide]
1. [SCSS styleguide][scss-styleguide]
1. [Shell commands](doc/development/shell_commands.md) created by GitLab
contributors to enhance security
@@ -549,7 +548,8 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[rss-naming]: https://github.com/bbatsov/ruby-style-guide/blob/master/README.md#naming
[changelog]: doc/development/changelog.md "Generate a changelog entry"
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
-[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
+[js-styleguide]: doc/development/fe_guide/style_guide_js.md "JavaScript styleguide"
+[scss-styleguide]: doc/development/fe_guide/style_guide_scss.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
[license-finder-doc]: doc/development/licensing.md
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 0d91a54c7d4..1d0ba9ea182 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.3.0
+0.4.0
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 347f5833ee6..9df886c42a1 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-1.4.1
+1.4.2
diff --git a/Gemfile b/Gemfile
index 6af27ce0f3e..c4368d79d5d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -244,7 +244,7 @@ gem 'net-ssh', '~> 3.0.1'
gem 'base32', '~> 0.3.0'
# Sentry integration
-gem 'sentry-raven', '~> 2.0.0'
+gem 'sentry-raven', '~> 2.4.0'
gem 'premailer-rails', '~> 1.9.0'
@@ -257,15 +257,14 @@ end
group :development do
gem 'foreman', '~> 0.78.0'
- gem 'brakeman', '~> 3.4.0', require: false
+ gem 'brakeman', '~> 3.6.0', require: false
gem 'letter_opener_web', '~> 1.3.0'
- gem 'bullet', '~> 5.2.0', require: false
+ gem 'bullet', '~> 5.5.0', require: false
gem 'rblineprof', '~> 0.3.6', platform: :mri, require: false
- gem 'web-console', '~> 2.0'
# Better errors handler
- gem 'better_errors', '~> 1.0.1'
+ gem 'better_errors', '~> 2.1.0'
gem 'binding_of_caller', '~> 0.7.2'
# thin instead webrick
@@ -297,7 +296,7 @@ group :development, :test do
gem 'capybara-screenshot', '~> 1.0.0'
gem 'poltergeist', '~> 1.9.0'
- gem 'spring', '~> 1.7.0'
+ gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
gem 'spring-commands-spinach', '~> 1.1.0'
@@ -305,8 +304,8 @@ group :development, :test do
gem 'rubocop-rspec', '~> 1.12.0', require: false
gem 'scss_lint', '~> 0.47.0', require: false
gem 'haml_lint', '~> 0.21.0', require: false
- gem 'simplecov', '0.12.0', require: false
- gem 'flay', '~> 2.6.1', require: false
+ gem 'simplecov', '~> 0.14.0', require: false
+ gem 'flay', '~> 2.8.0', require: false
gem 'bundler-audit', '~> 0.5.0', require: false
gem 'benchmark-ips', '~> 2.3.0', require: false
@@ -323,10 +322,11 @@ group :test do
gem 'shoulda-matchers', '~> 2.8.0', require: false
gem 'email_spec', '~> 1.6.0'
gem 'json-schema', '~> 2.6.2'
- gem 'webmock', '~> 1.21.0'
+ gem 'webmock', '~> 1.24.0'
gem 'test_after_commit', '~> 1.1'
gem 'sham_rack', '~> 1.3.6'
gem 'timecop', '~> 0.8.0'
+ gem 'concurrent-ruby', '~> 1.0.5'
end
gem 'octokit', '~> 4.6.2'
diff --git a/Gemfile.lock b/Gemfile.lock
index 043ca4f8800..2cb0e88962a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -75,19 +75,20 @@ GEM
base32 (0.3.2)
bcrypt (3.1.11)
benchmark-ips (2.3.0)
- better_errors (1.0.1)
+ better_errors (2.1.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
+ rack (>= 0.9.0)
bindata (2.3.5)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
bootstrap-sass (3.3.6)
autoprefixer-rails (>= 5.2.1)
sass (>= 3.3.4)
- brakeman (3.4.1)
+ brakeman (3.6.1)
browser (2.2.0)
builder (3.2.3)
- bullet (5.2.0)
+ bullet (5.5.1)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.10.0)
bundler-audit (0.5.0)
@@ -101,7 +102,7 @@ GEM
rack (>= 1.0.0)
rack-test (>= 0.5.4)
xpath (~> 2.0)
- capybara-screenshot (1.0.11)
+ capybara-screenshot (1.0.14)
capybara (>= 1.0, < 3)
launchy
carrierwave (0.11.2)
@@ -117,7 +118,7 @@ GEM
numerizer (~> 0.1.1)
chunky_png (1.3.5)
cliver (0.3.2)
- coderay (1.1.0)
+ coderay (1.1.1)
coercible (1.0.0)
descendants_tracker (~> 0.0.1)
coffee-rails (4.1.1)
@@ -128,7 +129,7 @@ GEM
execjs
coffee-script-source (1.10.0)
colorize (0.7.7)
- concurrent-ruby (1.0.4)
+ concurrent-ruby (1.0.5)
connection_pool (2.2.1)
crack (0.4.3)
safe_yaml (~> 1.0.0)
@@ -200,7 +201,9 @@ GEM
multi_json
ffaker (2.4.0)
ffi (1.9.10)
- flay (2.6.1)
+ flay (2.8.1)
+ erubis (~> 2.7.0)
+ path_expander (~> 1.0)
ruby_parser (~> 3.0)
sexp_processor (~> 4.0)
flowdock (0.7.1)
@@ -340,6 +343,7 @@ GEM
temple (~> 0.7.6)
thor
tilt
+ hashdiff (0.3.2)
hashie (3.5.5)
health_check (2.6.0)
rails (>= 4.0)
@@ -518,6 +522,7 @@ GEM
activerecord (>= 4.0, < 5.1)
parser (2.4.0.0)
ast (~> 2.2)
+ path_expander (1.0.1)
pg (0.18.4)
poltergeist (1.9.0)
capybara (~> 2.1)
@@ -532,14 +537,14 @@ GEM
premailer-rails (1.9.2)
actionmailer (>= 3, < 6)
premailer (~> 1.7, >= 1.7.9)
- pry (0.10.3)
+ pry (0.10.4)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
- pry-byebug (3.4.1)
+ pry-byebug (3.4.2)
byebug (~> 9.0)
pry (~> 0.10)
- pry-rails (0.3.4)
+ pry-rails (0.3.5)
pry (>= 0.9.10)
pyu-ruby-sasl (0.0.3.3)
rack (1.6.5)
@@ -671,7 +676,7 @@ GEM
ruby-progressbar (1.8.1)
ruby-saml (1.4.1)
nokogiri (>= 1.5.10)
- ruby_parser (3.8.2)
+ ruby_parser (3.8.4)
sexp_processor (~> 4.1)
rubyntlm (0.5.2)
rubypants (0.2.0)
@@ -700,10 +705,10 @@ GEM
activesupport (>= 3.1)
select2-rails (3.5.9.3)
thor (~> 0.14)
- sentry-raven (2.0.2)
- faraday (>= 0.7.6, < 0.10.x)
+ sentry-raven (2.4.0)
+ faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
- sexp_processor (4.7.0)
+ sexp_processor (4.8.0)
sham_rack (1.3.6)
rack
shoulda-matchers (2.8.0)
@@ -724,7 +729,7 @@ GEM
faraday (~> 0.9)
jwt (~> 1.5)
multi_json (~> 1.10)
- simplecov (0.12.0)
+ simplecov (0.14.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
@@ -741,7 +746,8 @@ GEM
spinach (>= 0.4)
spinach-rerun-reporter (0.0.2)
spinach (~> 0.8)
- spring (1.7.2)
+ spring (2.0.1)
+ activesupport (>= 4.2)
spring-commands-rspec (1.0.4)
spring (>= 0.9.1)
spring-commands-spinach (1.1.0)
@@ -813,14 +819,10 @@ GEM
vmstat (2.3.0)
warden (1.2.6)
rack (>= 1.0)
- web-console (2.3.0)
- activemodel (>= 4.0)
- binding_of_caller (>= 0.7.2)
- railties (>= 4.0)
- sprockets-rails (>= 2.0, < 4.0)
- webmock (1.21.0)
+ webmock (1.24.6)
addressable (>= 2.3.6)
crack (>= 0.3.2)
+ hashdiff
webpack-rails (0.9.9)
rails (>= 3.2.0)
websocket-driver (0.6.3)
@@ -854,12 +856,12 @@ DEPENDENCIES
babosa (~> 1.0.2)
base32 (~> 0.3.0)
benchmark-ips (~> 2.3.0)
- better_errors (~> 1.0.1)
+ better_errors (~> 2.1.0)
binding_of_caller (~> 0.7.2)
bootstrap-sass (~> 3.3.0)
- brakeman (~> 3.4.0)
+ brakeman (~> 3.6.0)
browser (~> 2.2)
- bullet (~> 5.2.0)
+ bullet (~> 5.5.0)
bundler-audit (~> 0.5.0)
capybara (~> 2.6.2)
capybara-screenshot (~> 1.0.0)
@@ -868,6 +870,7 @@ DEPENDENCIES
chronic (~> 0.10.2)
chronic_duration (~> 0.10.6)
coffee-rails (~> 4.1.0)
+ concurrent-ruby (~> 1.0.5)
connection_pool (~> 2.0)
creole (~> 0.5.0)
d3_rails (~> 3.5.0)
@@ -884,7 +887,7 @@ DEPENDENCIES
email_spec (~> 1.6.0)
factory_girl_rails (~> 4.7.0)
ffaker (~> 2.4)
- flay (~> 2.6.1)
+ flay (~> 2.8.0)
fog-aws (~> 0.9)
fog-core (~> 1.40)
fog-google (~> 0.5)
@@ -990,18 +993,18 @@ DEPENDENCIES
scss_lint (~> 0.47.0)
seed-fu (~> 2.3.5)
select2-rails (~> 3.5.9)
- sentry-raven (~> 2.0.0)
+ sentry-raven (~> 2.4.0)
settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6)
shoulda-matchers (~> 2.8.0)
sidekiq (~> 4.2.7)
sidekiq-cron (~> 0.4.4)
sidekiq-limit_fetch (~> 3.4)
- simplecov (= 0.12.0)
+ simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1)
spinach-rails (~> 0.2.1)
spinach-rerun-reporter (~> 0.0.2)
- spring (~> 1.7.0)
+ spring (~> 2.0.0)
spring-commands-rspec (~> 1.0.4)
spring-commands-spinach (~> 1.1.0)
sprockets (~> 3.7.0)
@@ -1022,8 +1025,7 @@ DEPENDENCIES
version_sorter (~> 2.1.0)
virtus (~> 1.0.1)
vmstat (~> 2.3.0)
- web-console (~> 2.0)
- webmock (~> 1.21.0)
+ webmock (~> 1.24.0)
webpack-rails (~> 0.9.9)
wikicloth (= 0.8.1)
diff --git a/README.md b/README.md
index 09e08adbb73..f0e3b52ef6f 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,15 @@
# GitLab
[![Build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
-[![CE coverage report](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby)
+[![Overall test coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg)](https://gitlab.com/gitlab-org/gitlab-ce/pipelines)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Core Infrastructure Initiative Best Practices](https://bestpractices.coreinfrastructure.org/projects/42/badge)](https://bestpractices.coreinfrastructure.org/projects/42)
+## Test coverage
+
+- [![Ruby coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=coverage)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-ruby) Ruby
+- [![JavaScript coverage](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/coverage.svg?job=rake+karma)](https://gitlab-org.gitlab.io/gitlab-ce/coverage-javascript) JavaScript
+
## Canonical source
The canonical source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/).
diff --git a/app/assets/images/icon-merge-request-unmerged.svg b/app/assets/images/icon-merge-request-unmerged.svg
index c4d8e65122d..d53a7470243 100644
--- a/app/assets/images/icon-merge-request-unmerged.svg
+++ b/app/assets/images/icon-merge-request-unmerged.svg
@@ -1 +1 @@
-<svg width="12" height="15" viewBox="0 0 12 15" xmlns="http://www.w3.org/2000/svg"><path d="M10.267 11.028V5.167c-.028-.728-.318-1.372-.878-1.923-.56-.55-1.194-.85-1.922-.877h-.934V.5l-2.8 2.8 2.8 2.8V4.233h.934a.976.976 0 0 1 .644.29.88.88 0 0 1 .289.644v5.861a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472zM3.733 3.3a1.86 1.86 0 0 0-1.866-1.867 1.86 1.86 0 0 0-.934 3.472v6.123a1.86 1.86 0 0 0 .933 3.472 1.86 1.86 0 0 0 .934-3.472V4.905c.55-.317.933-.914.933-1.605z" fill-rule="nonzero"/></svg>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m5 5.563v4.875c1.024.4 1.75 1.397 1.75 2.563 0 1.519-1.231 2.75-2.75 2.75-1.519 0-2.75-1.231-2.75-2.75 0-1.166.726-2.162 1.75-2.563v-4.875c-1.024-.4-1.75-1.397-1.75-2.563 0-1.519 1.231-2.75 2.75-2.75 1.519 0 2.75 1.231 2.75 2.75 0 1.166-.726 2.162-1.75 2.563m-1 8.687c.69 0 1.25-.56 1.25-1.25 0-.69-.56-1.25-1.25-1.25-.69 0-1.25.56-1.25 1.25 0 .69.56 1.25 1.25 1.25m0-10c.69 0 1.25-.56 1.25-1.25 0-.69-.56-1.25-1.25-1.25-.69 0-1.25.56-1.25 1.25 0 .69.56 1.25 1.25 1.25"/><path d="m10.501 2c1.381.001 2.499 1.125 2.499 2.506v5.931c1.024.4 1.75 1.397 1.75 2.563 0 1.519-1.231 2.75-2.75 2.75-1.519 0-2.75-1.231-2.75-2.75 0-1.166.726-2.162 1.75-2.563v-5.931c0-.279-.225-.506-.499-.506v.926c0 .346-.244.474-.569.271l-2.952-1.844c-.314-.196-.325-.507 0-.71l2.952-1.844c.314-.196.569-.081.569.271v.93m1.499 12.25c.69 0 1.25-.56 1.25-1.25 0-.69-.56-1.25-1.25-1.25-.69 0-1.25.56-1.25 1.25 0 .69.56 1.25 1.25 1.25"/></svg> \ No newline at end of file
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js
index aebda7780e1..d816df831eb 100644
--- a/app/assets/javascripts/activities.js
+++ b/app/assets/javascripts/activities.js
@@ -1,6 +1,7 @@
/* eslint-disable no-param-reassign, class-methods-use-this */
/* global Pager */
-/* global Cookies */
+
+import Cookies from 'js-cookie';
class Activities {
constructor() {
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 9349918f7a0..c743dd551d7 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,4 +1,4 @@
-/* global Cookies */
+import Cookies from 'js-cookie';
import emojiMap from 'emojis/digests.json';
import emojiAliases from 'emojis/aliases.json';
diff --git a/app/assets/javascripts/behaviors/toggler_behavior.js b/app/assets/javascripts/behaviors/toggler_behavior.js
index 92f3bb3ff52..86927314dd4 100644
--- a/app/assets/javascripts/behaviors/toggler_behavior.js
+++ b/app/assets/javascripts/behaviors/toggler_behavior.js
@@ -24,7 +24,7 @@
$('body').on('click', '.js-toggle-button', function(e) {
toggleContainer($(this).closest('.js-toggle-container'));
- const targetTag = e.target.tagName.toLowerCase();
+ const targetTag = e.currentTarget.tagName.toLowerCase();
if (targetTag === 'a' || targetTag === 'button') {
e.preventDefault();
}
diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js b/app/assets/javascripts/blob/blob_ci_yaml.js
deleted file mode 100644
index ec1c018424d..00000000000
--- a/app/assets/javascripts/blob/blob_ci_yaml.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/* eslint-disable no-param-reassign, comma-dangle */
-/* global Api */
-
-require('./template_selector');
-
-((global) => {
- class BlobCiYamlSelector extends gl.TemplateSelector {
- requestFile(query) {
- return Api.gitlabCiYml(query.name, this.requestFileSuccess.bind(this));
- }
-
- requestFileSuccess(file) {
- return super.requestFileSuccess(file);
- }
- }
-
- global.BlobCiYamlSelector = BlobCiYamlSelector;
-
- class BlobCiYamlSelectors {
- constructor({ editor, $dropdowns } = {}) {
- this.editor = editor;
- this.$dropdowns = $dropdowns || $('.js-gitlab-ci-yml-selector');
- this.initSelectors();
- }
-
- initSelectors() {
- const editor = this.editor;
- this.$dropdowns.each((i, dropdown) => {
- const $dropdown = $(dropdown);
- return new BlobCiYamlSelector({
- editor,
- pattern: /(.gitlab-ci.yml)/,
- data: $dropdown.data('data'),
- wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
- dropdown: $dropdown
- });
- });
- }
- }
-
- global.BlobCiYamlSelectors = BlobCiYamlSelectors;
-})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/blob/blob_dockerfile_selector.js b/app/assets/javascripts/blob/blob_dockerfile_selector.js
deleted file mode 100644
index d4f60cc6ecd..00000000000
--- a/app/assets/javascripts/blob/blob_dockerfile_selector.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* global Api */
-
-require('./template_selector');
-
-(() => {
- const global = window.gl || (window.gl = {});
-
- class BlobDockerfileSelector extends gl.TemplateSelector {
- requestFile(query) {
- return Api.dockerfileYml(query.name, this.requestFileSuccess.bind(this));
- }
-
- requestFileSuccess(file) {
- return super.requestFileSuccess(file);
- }
- }
-
- global.BlobDockerfileSelector = BlobDockerfileSelector;
-})();
diff --git a/app/assets/javascripts/blob/blob_dockerfile_selectors.js b/app/assets/javascripts/blob/blob_dockerfile_selectors.js
deleted file mode 100644
index 9cee79fa5d5..00000000000
--- a/app/assets/javascripts/blob/blob_dockerfile_selectors.js
+++ /dev/null
@@ -1,27 +0,0 @@
-(() => {
- const global = window.gl || (window.gl = {});
-
- class BlobDockerfileSelectors {
- constructor({ editor, $dropdowns } = {}) {
- this.editor = editor;
- this.$dropdowns = $dropdowns || $('.js-dockerfile-selector');
- this.initSelectors();
- }
-
- initSelectors() {
- const editor = this.editor;
- this.$dropdowns.each((i, dropdown) => {
- const $dropdown = $(dropdown);
- return new gl.BlobDockerfileSelector({
- editor,
- pattern: /(Dockerfile)/,
- data: $dropdown.data('data'),
- wrapper: $dropdown.closest('.js-dockerfile-selector-wrap'),
- dropdown: $dropdown,
- });
- });
- }
- }
-
- global.BlobDockerfileSelectors = BlobDockerfileSelectors;
-})();
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js b/app/assets/javascripts/blob/blob_file_dropzone.js
index 8f6bf162d6e..c9fe23aec75 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js
@@ -1,66 +1,63 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, one-var, no-var, one-var-declaration-per-line, camelcase, object-shorthand, quotes, comma-dangle, prefer-arrow-callback, no-unused-vars, prefer-template, no-useless-escape, no-alert, max-len */
+/* eslint-disable func-names, object-shorthand, prefer-arrow-callback */
/* global Dropzone */
-(function() {
- this.BlobFileDropzone = (function() {
- function BlobFileDropzone(form, method) {
- var dropzone, form_dropzone, submitButton;
- form_dropzone = form.find('.dropzone');
- Dropzone.autoDiscover = false;
- dropzone = form_dropzone.dropzone({
- autoDiscover: false,
- autoProcessQueue: false,
- url: form.attr('action'),
- // Rails uses a hidden input field for PUT
- // http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
- method: method,
- clickable: true,
- uploadMultiple: false,
- paramName: "file",
- maxFilesize: gon.max_file_size || 10,
- parallelUploads: 1,
- maxFiles: 1,
- addRemoveLinks: true,
- previewsContainer: '.dropzone-previews',
- headers: {
- "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
- },
- init: function() {
- this.on('addedfile', function(file) {
- $('.dropzone-alerts').html('').hide();
- });
- this.on('success', function(header, response) {
- window.location.href = response.filePath;
- });
- this.on('maxfilesexceeded', function(file) {
- this.removeFile(file);
- });
- return this.on('sending', function(file, xhr, formData) {
- formData.append('target_branch', form.find('input[name="target_branch"]').val());
- formData.append('create_merge_request', form.find('.js-create-merge-request').val());
- formData.append('commit_message', form.find('.js-commit-message').val());
- });
- },
- // Override behavior of adding error underneath preview
- error: function(file, errorMessage) {
- var stripped;
- stripped = $("<div/>").html(errorMessage).text();
- $('.dropzone-alerts').html('Error uploading file: \"' + stripped + '\"').show();
+export default class BlobFileDropzone {
+ constructor(form, method) {
+ const formDropzone = form.find('.dropzone');
+ Dropzone.autoDiscover = false;
+
+ const dropzone = formDropzone.dropzone({
+ autoDiscover: false,
+ autoProcessQueue: false,
+ url: form.attr('action'),
+ // Rails uses a hidden input field for PUT
+ // http://stackoverflow.com/questions/21056482/how-to-set-method-put-in-form-tag-in-rails
+ method: method,
+ clickable: true,
+ uploadMultiple: false,
+ paramName: 'file',
+ maxFilesize: gon.max_file_size || 10,
+ parallelUploads: 1,
+ maxFiles: 1,
+ addRemoveLinks: true,
+ previewsContainer: '.dropzone-previews',
+ headers: {
+ 'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content'),
+ },
+ init: function () {
+ this.on('addedfile', function () {
+ $('.dropzone-alerts').html('').hide();
+ });
+ this.on('success', function (header, response) {
+ window.location.href = response.filePath;
+ });
+ this.on('maxfilesexceeded', function (file) {
this.removeFile(file);
- }
- });
- submitButton = form.find('#submit-all')[0];
- submitButton.addEventListener('click', function(e) {
- e.preventDefault();
- e.stopPropagation();
- if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
- alert("Please select a file");
- }
- dropzone[0].dropzone.processQueue();
- return false;
- });
- }
+ });
+ this.on('sending', function (file, xhr, formData) {
+ formData.append('target_branch', form.find('input[name="target_branch"]').val());
+ formData.append('create_merge_request', form.find('.js-create-merge-request').val());
+ formData.append('commit_message', form.find('.js-commit-message').val());
+ });
+ },
+ // Override behavior of adding error underneath preview
+ error: function (file, errorMessage) {
+ const stripped = $('<div/>').html(errorMessage).text();
+ $('.dropzone-alerts').html(`Error uploading file: "${stripped}"`).show();
+ this.removeFile(file);
+ },
+ });
- return BlobFileDropzone;
- })();
-}).call(window);
+ const submitButton = form.find('#submit-all')[0];
+ submitButton.addEventListener('click', function (e) {
+ e.preventDefault();
+ e.stopPropagation();
+ if (dropzone[0].dropzone.getQueuedFiles().length === 0) {
+ // eslint-disable-next-line no-alert
+ alert('Please select a file');
+ }
+ dropzone[0].dropzone.processQueue();
+ return false;
+ });
+ }
+}
diff --git a/app/assets/javascripts/blob/blob_gitignore_selector.js b/app/assets/javascripts/blob/blob_gitignore_selector.js
deleted file mode 100644
index de20eab9cd1..00000000000
--- a/app/assets/javascripts/blob/blob_gitignore_selector.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params */
-/* global Api */
-
-require('./template_selector');
-
-(function() {
- var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- hasProp = {}.hasOwnProperty;
-
- this.BlobGitignoreSelector = (function(superClass) {
- extend(BlobGitignoreSelector, superClass);
-
- function BlobGitignoreSelector() {
- return BlobGitignoreSelector.__super__.constructor.apply(this, arguments);
- }
-
- BlobGitignoreSelector.prototype.requestFile = function(query) {
- return Api.gitignoreText(query.name, this.requestFileSuccess.bind(this));
- };
-
- return BlobGitignoreSelector;
- })(gl.TemplateSelector);
-}).call(window);
diff --git a/app/assets/javascripts/blob/blob_gitignore_selectors.js b/app/assets/javascripts/blob/blob_gitignore_selectors.js
deleted file mode 100644
index 43e5c0a5641..00000000000
--- a/app/assets/javascripts/blob/blob_gitignore_selectors.js
+++ /dev/null
@@ -1,26 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-cond-assign, no-sequences, comma-dangle, max-len */
-/* global BlobGitignoreSelector */
-
-(function() {
- this.BlobGitignoreSelectors = (function() {
- function BlobGitignoreSelectors(opts) {
- var ref;
- this.$dropdowns = (ref = opts.$dropdowns) != null ? ref : $('.js-gitignore-selector'), this.editor = opts.editor;
- this.$dropdowns.each((function(_this) {
- return function(i, dropdown) {
- var $dropdown;
- $dropdown = $(dropdown);
- return new BlobGitignoreSelector({
- pattern: /(.gitignore)/,
- data: $dropdown.data('data'),
- wrapper: $dropdown.closest('.js-gitignore-selector-wrap'),
- dropdown: $dropdown,
- editor: _this.editor
- });
- };
- })(this));
- }
-
- return BlobGitignoreSelectors;
- })();
-}).call(window);
diff --git a/app/assets/javascripts/blob/blob_license_selector.js b/app/assets/javascripts/blob/blob_license_selector.js
deleted file mode 100644
index b582052a76e..00000000000
--- a/app/assets/javascripts/blob/blob_license_selector.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, max-len, one-var, no-var, no-restricted-syntax, vars-on-top, no-use-before-define, no-param-reassign, new-cap, no-underscore-dangle, wrap-iife, prefer-rest-params, comma-dangle */
-/* global Api */
-
-require('./template_selector');
-
-(function() {
- var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- hasProp = {}.hasOwnProperty;
-
- this.BlobLicenseSelector = (function(superClass) {
- extend(BlobLicenseSelector, superClass);
-
- function BlobLicenseSelector() {
- return BlobLicenseSelector.__super__.constructor.apply(this, arguments);
- }
-
- BlobLicenseSelector.prototype.requestFile = function(query) {
- var data;
- data = {
- project: this.dropdown.data('project'),
- fullname: this.dropdown.data('fullname')
- };
- return Api.licenseText(query.id, data, this.requestFileSuccess.bind(this));
- };
-
- return BlobLicenseSelector;
- })(gl.TemplateSelector);
-}).call(window);
diff --git a/app/assets/javascripts/blob/blob_license_selectors.js b/app/assets/javascripts/blob/blob_license_selectors.js
deleted file mode 100644
index c5067b0feae..00000000000
--- a/app/assets/javascripts/blob/blob_license_selectors.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable no-unused-vars, no-param-reassign */
-/* global BlobLicenseSelector */
-
-((global) => {
- class BlobLicenseSelectors {
- constructor({ $dropdowns, editor }) {
- this.$dropdowns = $('.js-license-selector');
- this.editor = editor;
- this.$dropdowns.each((i, dropdown) => {
- const $dropdown = $(dropdown);
- return new BlobLicenseSelector({
- editor,
- pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
- data: $dropdown.data('data'),
- wrapper: $dropdown.closest('.js-license-selector-wrap'),
- dropdown: $dropdown,
- });
- });
- }
- }
-
- global.BlobLicenseSelectors = BlobLicenseSelectors;
-})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/blob/template_selector.js b/app/assets/javascripts/blob/template_selector.js
deleted file mode 100644
index 7e03ec3b391..00000000000
--- a/app/assets/javascripts/blob/template_selector.js
+++ /dev/null
@@ -1,101 +0,0 @@
-/* eslint-disable comma-dangle, object-shorthand, func-names, space-before-function-paren, arrow-parens, no-unused-vars, class-methods-use-this, no-var, consistent-return, no-param-reassign, max-len */
-
-((global) => {
- class TemplateSelector {
- constructor({ dropdown, data, pattern, wrapper, editor, fileEndpoint, $input } = {}) {
- this.onClick = this.onClick.bind(this);
- this.dropdown = dropdown;
- this.data = data;
- this.pattern = pattern;
- this.wrapper = wrapper;
- this.editor = editor;
- this.fileEndpoint = fileEndpoint;
- this.$input = $input || $('#file_name');
- this.dropdownIcon = $('.fa-chevron-down', this.dropdown);
- this.buildDropdown();
- this.bindEvents();
- this.onFilenameUpdate();
-
- this.autosizeUpdateEvent = document.createEvent('Event');
- this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
- }
-
- buildDropdown() {
- return this.dropdown.glDropdown({
- data: this.data,
- filterable: true,
- selectable: true,
- toggleLabel: this.toggleLabel,
- search: {
- fields: ['name']
- },
- clicked: this.onClick,
- text: function(item) {
- return item.name;
- }
- });
- }
-
- bindEvents() {
- return this.$input.on('keyup blur', (e) => this.onFilenameUpdate());
- }
-
- toggleLabel(item) {
- return item.name;
- }
-
- onFilenameUpdate() {
- var filenameMatches;
- if (!this.$input.length) {
- return;
- }
- filenameMatches = this.pattern.test(this.$input.val().trim());
- if (!filenameMatches) {
- this.wrapper.addClass('hidden');
- return;
- }
- return this.wrapper.removeClass('hidden');
- }
-
- onClick(item, el, e) {
- e.preventDefault();
- return this.requestFile(item);
- }
-
- requestFile(item) {
- // This `requestFile` method is an abstract method that should
- // be added by all subclasses.
- }
-
- // To be implemented on the extending class
- // e.g.
- // Api.gitignoreText item.name, @requestFileSuccess.bind(@)
- requestFileSuccess(file, { skipFocus } = {}) {
- if (!file) return;
-
- const oldValue = this.editor.getValue();
- const newValue = file.content;
-
- this.editor.setValue(newValue, 1);
- if (!skipFocus) this.editor.focus();
-
- if (this.editor instanceof jQuery) {
- this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
- }
- }
-
- startLoadingSpinner() {
- this.dropdownIcon
- .addClass('fa-spinner fa-spin')
- .removeClass('fa-chevron-down');
- }
-
- stopLoadingSpinner() {
- this.dropdownIcon
- .addClass('fa-chevron-down')
- .removeClass('fa-spinner fa-spin');
- }
- }
-
- global.TemplateSelector = TemplateSelector;
-})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selector.js b/app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selector.js
new file mode 100644
index 00000000000..5a5954e7751
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selector.js
@@ -0,0 +1,9 @@
+/* global Api */
+
+import TemplateSelector from './template_selector';
+
+export default class BlobCiYamlSelector extends TemplateSelector {
+ requestFile(query) {
+ return Api.gitlabCiYml(query.name, (file, config) => this.setEditorContent(file, config));
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selectors.js b/app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selectors.js
new file mode 100644
index 00000000000..7a4d6a42a03
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_ci_yaml_selectors.js
@@ -0,0 +1,23 @@
+/* global Api */
+
+import BlobCiYamlSelector from './blob_ci_yaml_selector';
+
+export default class BlobCiYamlSelectors {
+ constructor({ editor, $dropdowns }) {
+ this.$dropdowns = $dropdowns || $('.js-gitlab-ci-yml-selector');
+ this.initSelectors(editor);
+ }
+
+ initSelectors(editor) {
+ this.$dropdowns.each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+ return new BlobCiYamlSelector({
+ editor,
+ pattern: /(.gitlab-ci.yml)/,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
+ dropdown: $dropdown,
+ });
+ });
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_dockerfile_selector.js b/app/assets/javascripts/blob/template_selectors/blob_dockerfile_selector.js
new file mode 100644
index 00000000000..19f8820a0cb
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_dockerfile_selector.js
@@ -0,0 +1,9 @@
+/* global Api */
+
+import TemplateSelector from './template_selector';
+
+export default class BlobDockerfileSelector extends TemplateSelector {
+ requestFile(query) {
+ return Api.dockerfileYml(query.name, (file, config) => this.setEditorContent(file, config));
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_dockerfile_selectors.js b/app/assets/javascripts/blob/template_selectors/blob_dockerfile_selectors.js
new file mode 100644
index 00000000000..da067035b43
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_dockerfile_selectors.js
@@ -0,0 +1,23 @@
+import BlobDockerfileSelector from './blob_dockerfile_selector';
+
+export default class BlobDockerfileSelectors {
+ constructor({ editor, $dropdowns }) {
+ this.editor = editor;
+ this.$dropdowns = $dropdowns || $('.js-dockerfile-selector');
+ this.initSelectors();
+ }
+
+ initSelectors() {
+ const editor = this.editor;
+ this.$dropdowns.each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+ return new BlobDockerfileSelector({
+ editor,
+ pattern: /(Dockerfile)/,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-dockerfile-selector-wrap'),
+ dropdown: $dropdown,
+ });
+ });
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_gitignore_selector.js b/app/assets/javascripts/blob/template_selectors/blob_gitignore_selector.js
new file mode 100644
index 00000000000..0b6b02fc2b3
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_gitignore_selector.js
@@ -0,0 +1,9 @@
+/* global Api */
+
+import TemplateSelector from './template_selector';
+
+export default class BlobGitignoreSelector extends TemplateSelector {
+ requestFile(query) {
+ return Api.gitignoreText(query.name, (file, config) => this.setEditorContent(file, config));
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_gitignore_selectors.js b/app/assets/javascripts/blob/template_selectors/blob_gitignore_selectors.js
new file mode 100644
index 00000000000..dc485d97677
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_gitignore_selectors.js
@@ -0,0 +1,23 @@
+import BlobGitignoreSelector from './blob_gitignore_selector';
+
+export default class BlobGitignoreSelectors {
+ constructor({ editor, $dropdowns }) {
+ this.$dropdowns = $dropdowns || $('.js-gitignore-selector');
+ this.editor = editor;
+ this.initSelectors();
+ }
+
+ initSelectors() {
+ this.$dropdowns.each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+
+ return new BlobGitignoreSelector({
+ pattern: /(.gitignore)/,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-gitignore-selector-wrap'),
+ dropdown: $dropdown,
+ editor: this.editor,
+ });
+ });
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_license_selector.js b/app/assets/javascripts/blob/template_selectors/blob_license_selector.js
new file mode 100644
index 00000000000..e9cb31cc2dc
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_license_selector.js
@@ -0,0 +1,13 @@
+/* global Api */
+
+import TemplateSelector from './template_selector';
+
+export default class BlobLicenseSelector extends TemplateSelector {
+ requestFile(query) {
+ const data = {
+ project: this.dropdown.data('project'),
+ fullname: this.dropdown.data('fullname'),
+ };
+ return Api.licenseText(query.id, data, (file, config) => this.setEditorContent(file, config));
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/blob_license_selectors.js b/app/assets/javascripts/blob/template_selectors/blob_license_selectors.js
new file mode 100644
index 00000000000..a44f4f78b2d
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/blob_license_selectors.js
@@ -0,0 +1,24 @@
+/* eslint-disable no-unused-vars, no-param-reassign */
+
+import BlobLicenseSelector from './blob_license_selector';
+
+export default class BlobLicenseSelectors {
+ constructor({ $dropdowns, editor }) {
+ this.$dropdowns = $dropdowns || $('.js-license-selector');
+ this.initSelectors(editor);
+ }
+
+ initSelectors(editor) {
+ this.$dropdowns.each((i, dropdown) => {
+ const $dropdown = $(dropdown);
+
+ return new BlobLicenseSelector({
+ editor,
+ pattern: /^(.+\/)?(licen[sc]e|copying)($|\.)/i,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-license-selector-wrap'),
+ dropdown: $dropdown,
+ });
+ });
+ }
+}
diff --git a/app/assets/javascripts/blob/template_selectors/template_selector.js b/app/assets/javascripts/blob/template_selectors/template_selector.js
new file mode 100644
index 00000000000..d7c1c32efbd
--- /dev/null
+++ b/app/assets/javascripts/blob/template_selectors/template_selector.js
@@ -0,0 +1,92 @@
+/* eslint-disable class-methods-use-this, no-unused-vars */
+
+export default class TemplateSelector {
+ constructor({ dropdown, data, pattern, wrapper, editor, $input } = {}) {
+ this.pattern = pattern;
+ this.editor = editor;
+ this.dropdown = dropdown;
+ this.$dropdownContainer = wrapper;
+ this.$filenameInput = $input || $('#file_name');
+ this.$dropdownIcon = $('.fa-chevron-down', dropdown);
+
+ this.initDropdown(dropdown, data);
+ this.listenForFilenameInput();
+ this.renderMatchedDropdown();
+ this.initAutosizeUpdateEvent();
+ }
+
+ initDropdown(dropdown, data) {
+ return $(dropdown).glDropdown({
+ data,
+ filterable: true,
+ selectable: true,
+ toggleLabel: item => item.name,
+ search: {
+ fields: ['name'],
+ },
+ clicked: (item, el, e) => this.fetchFileTemplate(item, el, e),
+ text: item => item.name,
+ });
+ }
+
+ initAutosizeUpdateEvent() {
+ this.autosizeUpdateEvent = document.createEvent('Event');
+ this.autosizeUpdateEvent.initEvent('autosize:update', true, false);
+ }
+
+ listenForFilenameInput() {
+ return this.$filenameInput.on('keyup blur', e => this.renderMatchedDropdown(e));
+ }
+
+ renderMatchedDropdown() {
+ if (!this.$filenameInput.length) {
+ return null;
+ }
+
+ const filenameMatches = this.pattern.test(this.$filenameInput.val().trim());
+
+ if (!filenameMatches) {
+ return this.$dropdownContainer.addClass('hidden');
+ }
+ return this.$dropdownContainer.removeClass('hidden');
+ }
+
+ fetchFileTemplate(item, el, e) {
+ e.preventDefault();
+ return this.requestFile(item);
+ }
+
+ requestFile(item) {
+ // This `requestFile` method is an abstract method that should
+ // be added by all subclasses.
+ }
+
+ // To be implemented on the extending class
+ // e.g. Api.gitlabCiYml(query.name, file => this.setEditorContent(file));
+
+ setEditorContent(file, { skipFocus } = {}) {
+ if (!file) return;
+
+ const newValue = file.content;
+
+ this.editor.setValue(newValue, 1);
+
+ if (!skipFocus) this.editor.focus();
+
+ if (this.editor instanceof jQuery) {
+ this.editor.get(0).dispatchEvent(this.autosizeUpdateEvent);
+ }
+ }
+
+ startLoadingSpinner() {
+ this.$dropdownIcon
+ .addClass('fa-spinner fa-spin')
+ .removeClass('fa-chevron-down');
+ }
+
+ stopLoadingSpinner() {
+ this.$dropdownIcon
+ .addClass('fa-chevron-down')
+ .removeClass('fa-spinner fa-spin');
+ }
+}
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
new file mode 100644
index 00000000000..c5deccf631e
--- /dev/null
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -0,0 +1,32 @@
+/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-new, max-len */
+/* global EditBlob */
+/* global NewCommitForm */
+
+import EditBlob from './edit_blob';
+import BlobFileDropzone from '../blob/blob_file_dropzone';
+
+$(() => {
+ const editBlobForm = $('.js-edit-blob-form');
+ const uploadBlobForm = $('.js-upload-blob-form');
+
+ if (editBlobForm.length) {
+ const urlRoot = editBlobForm.data('relative-url-root');
+ const assetsPath = editBlobForm.data('assets-prefix');
+ const blobLanguage = editBlobForm.data('blob-language');
+
+ new EditBlob(`${urlRoot}${assetsPath}`, blobLanguage);
+ new NewCommitForm(editBlobForm);
+ }
+
+ if (uploadBlobForm.length) {
+ const method = uploadBlobForm.data('method');
+
+ new BlobFileDropzone(uploadBlobForm, method);
+ new NewCommitForm(uploadBlobForm);
+
+ window.gl.utils.disableButtonIfEmptyField(
+ uploadBlobForm.find('.js-commit-message'),
+ '.btn-upload-file',
+ );
+ }
+});
diff --git a/app/assets/javascripts/blob_edit/blob_edit_bundle.js b/app/assets/javascripts/blob_edit/blob_edit_bundle.js
deleted file mode 100644
index 0436bbb0eaf..00000000000
--- a/app/assets/javascripts/blob_edit/blob_edit_bundle.js
+++ /dev/null
@@ -1,15 +0,0 @@
-/* eslint-disable func-names, space-before-function-paren, prefer-arrow-callback, no-var, quotes, vars-on-top, no-unused-vars, no-new, max-len */
-/* global EditBlob */
-/* global NewCommitForm */
-
-require('./edit_blob');
-
-(function() {
- $(function() {
- var url = $(".js-edit-blob-form").data("relative-url-root");
- url += $(".js-edit-blob-form").data("assets-prefix");
-
- var blob = new EditBlob(url, $('.js-edit-blob-form').data('blob-language'));
- new NewCommitForm($('.js-edit-blob-form'));
- });
-}).call(window);
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index a1127b9e30e..d3560d5df3b 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -1,88 +1,99 @@
-/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, camelcase, no-param-reassign, quotes, prefer-template, no-new, comma-dangle, one-var, one-var-declaration-per-line, prefer-arrow-callback, no-else-return, no-unused-vars, max-len */
/* global ace */
-/* global BlobGitignoreSelectors */
-
-(function() {
- var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
-
- this.EditBlob = (function() {
- function EditBlob(assets_path, ace_mode) {
- if (ace_mode == null) {
- ace_mode = null;
- }
- this.editModeLinkClickHandler = bind(this.editModeLinkClickHandler, this);
- ace.config.set("modePath", assets_path + "/ace");
- ace.config.loadModule("ace/ext/searchbox");
- this.editor = ace.edit("editor");
- this.editor.focus();
- if (ace_mode) {
- this.editor.getSession().setMode("ace/mode/" + ace_mode);
- }
- $('form').submit((function(_this) {
- return function() {
- return $("#file-content").val(_this.editor.getValue());
- };
- // Before a form submission, move the content from the Ace editor into the
- // submitted textarea
- })(this));
- this.initModePanesAndLinks();
- this.initSoftWrap();
- new gl.BlobLicenseSelectors({
- editor: this.editor
- });
+
+import BlobLicenseSelectors from '../blob/template_selectors/blob_license_selectors';
+import BlobGitignoreSelectors from '../blob/template_selectors/blob_gitignore_selectors';
+import BlobCiYamlSelectors from '../blob/template_selectors/blob_ci_yaml_selectors';
+import BlobDockerfileSelectors from '../blob/template_selectors/blob_dockerfile_selectors';
+
+export default class EditBlob {
+ constructor(assetsPath, aceMode) {
+ this.configureAceEditor(aceMode, assetsPath);
+ this.prepFileContentForSubmit();
+ this.initModePanesAndLinks();
+ this.initSoftWrap();
+ this.initFileSelectors();
+ }
+
+ configureAceEditor(aceMode, assetsPath) {
+ ace.config.set('modePath', `${assetsPath}/ace`);
+ ace.config.loadModule('ace/ext/searchbox');
+
+ this.editor = ace.edit('editor');
+ this.editor.focus();
+
+ if (aceMode) {
+ this.editor.getSession().setMode(`ace/mode/${aceMode}`);
+ }
+ }
+
+ prepFileContentForSubmit() {
+ $('form').submit(() => {
+ $('#file-content').val(this.editor.getValue());
+ });
+ }
+
+ initFileSelectors() {
+ this.blobTemplateSelectors = [
+ new BlobLicenseSelectors({
+ editor: this.editor,
+ }),
new BlobGitignoreSelectors({
- editor: this.editor
- });
- new gl.BlobCiYamlSelectors({
- editor: this.editor
- });
- new gl.BlobDockerfileSelectors({
- editor: this.editor
+ editor: this.editor,
+ }),
+ new BlobCiYamlSelectors({
+ editor: this.editor,
+ }),
+ new BlobDockerfileSelectors({
+ editor: this.editor,
+ }),
+ ];
+ }
+
+ initModePanesAndLinks() {
+ this.$editModePanes = $('.js-edit-mode-pane');
+ this.$editModeLinks = $('.js-edit-mode a');
+ this.$editModeLinks.on('click', e => this.editModeLinkClickHandler(e));
+ }
+
+ editModeLinkClickHandler(e) {
+ e.preventDefault();
+
+ const currentLink = $(e.target);
+ const paneId = currentLink.attr('href');
+ const currentPane = this.$editModePanes.filter(paneId);
+
+ this.$editModeLinks.parent().removeClass('active hover');
+
+ currentLink.parent().addClass('active hover');
+
+ this.$editModePanes.hide();
+
+ currentPane.fadeIn(200);
+
+ if (paneId === '#preview') {
+ this.$toggleButton.hide();
+ return $.post(currentLink.data('preview-url'), {
+ content: this.editor.getValue(),
+ }, (response) => {
+ currentPane.empty().append(response);
+ return currentPane.renderGFM();
});
}
- EditBlob.prototype.initModePanesAndLinks = function() {
- this.$editModePanes = $(".js-edit-mode-pane");
- this.$editModeLinks = $(".js-edit-mode a");
- return this.$editModeLinks.click(this.editModeLinkClickHandler);
- };
-
- EditBlob.prototype.editModeLinkClickHandler = function(event) {
- var currentLink, currentPane, paneId;
- event.preventDefault();
- currentLink = $(event.target);
- paneId = currentLink.attr("href");
- currentPane = this.$editModePanes.filter(paneId);
- this.$editModeLinks.parent().removeClass("active hover");
- currentLink.parent().addClass("active hover");
- this.$editModePanes.hide();
- currentPane.fadeIn(200);
- if (paneId === "#preview") {
- this.$toggleButton.hide();
- return $.post(currentLink.data("preview-url"), {
- content: this.editor.getValue()
- }, function(response) {
- currentPane.empty().append(response);
- return currentPane.renderGFM();
- });
- } else {
- this.$toggleButton.show();
- return this.editor.focus();
- }
- };
-
- EditBlob.prototype.initSoftWrap = function() {
- this.isSoftWrapped = false;
- this.$toggleButton = $('.soft-wrap-toggle');
- this.$toggleButton.on('click', this.toggleSoftWrap.bind(this));
- };
-
- EditBlob.prototype.toggleSoftWrap = function(e) {
- this.isSoftWrapped = !this.isSoftWrapped;
- this.$toggleButton.toggleClass('soft-wrap-active', this.isSoftWrapped);
- this.editor.getSession().setUseWrapMode(this.isSoftWrapped);
- };
-
- return EditBlob;
- })();
-}).call(window);
+ this.$toggleButton.show();
+
+ return this.editor.focus();
+ }
+
+ initSoftWrap() {
+ this.isSoftWrapped = false;
+ this.$toggleButton = $('.soft-wrap-toggle');
+ this.$toggleButton.on('click', () => this.toggleSoftWrap());
+ }
+
+ toggleSoftWrap() {
+ this.isSoftWrapped = !this.isSoftWrapped;
+ this.$toggleButton.toggleClass('soft-wrap-active', this.isSoftWrapped);
+ this.editor.getSession().setUseWrapMode(this.isSoftWrapped);
+ }
+}
diff --git a/app/assets/javascripts/boards/boards_bundle.js b/app/assets/javascripts/boards/boards_bundle.js
index 3874c2819a5..149bfbc8e8b 100644
--- a/app/assets/javascripts/boards/boards_bundle.js
+++ b/app/assets/javascripts/boards/boards_bundle.js
@@ -1,12 +1,11 @@
/* eslint-disable one-var, quote-props, comma-dangle, space-before-function-paren */
-/* global Vue */
/* global BoardService */
+import Vue from 'vue';
+import VueResource from 'vue-resource';
import FilteredSearchBoards from './filtered_search_boards';
import eventHub from './eventhub';
-window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
require('./models/issue');
require('./models/label');
require('./models/list');
@@ -24,6 +23,8 @@ require('./components/new_list_dropdown');
require('./components/modal/index');
require('../vue_shared/vue_resource_interceptor');
+Vue.use(VueResource);
+
$(() => {
const $boardApp = document.getElementById('board-app');
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/board.js b/app/assets/javascripts/boards/components/board.js
index 67c0c419713..35b3205cca7 100644
--- a/app/assets/javascripts/boards/components/board.js
+++ b/app/assets/javascripts/boards/components/board.js
@@ -1,7 +1,7 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var */
-/* global Vue */
/* global Sortable */
+import Vue from 'vue';
import boardBlankState from './board_blank_state';
require('./board_delete');
diff --git a/app/assets/javascripts/boards/components/board_blank_state.js b/app/assets/javascripts/boards/components/board_blank_state.js
index 52893d4642b..3fc68457961 100644
--- a/app/assets/javascripts/boards/components/board_blank_state.js
+++ b/app/assets/javascripts/boards/components/board_blank_state.js
@@ -1,5 +1,7 @@
/* global ListLabel */
-/* global Cookies */
+
+import Cookies from 'js-cookie';
+
const Store = gl.issueBoards.BoardsStore;
export default {
diff --git a/app/assets/javascripts/boards/components/board_card.js b/app/assets/javascripts/boards/components/board_card.js
index 4b72090df31..9320848bcca 100644
--- a/app/assets/javascripts/boards/components/board_card.js
+++ b/app/assets/javascripts/boards/components/board_card.js
@@ -1,4 +1,3 @@
-/* global Vue */
require('./issue_card_inner');
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/components/board_delete.js b/app/assets/javascripts/boards/components/board_delete.js
index 861600424a5..af621cfd57f 100644
--- a/app/assets/javascripts/boards/components/board_delete.js
+++ b/app/assets/javascripts/boards/components/board_delete.js
@@ -1,5 +1,6 @@
/* eslint-disable comma-dangle, space-before-function-paren, no-alert */
-/* global Vue */
+
+import Vue from 'vue';
(() => {
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/boards/components/board_list.js b/app/assets/javascripts/boards/components/board_list.js
index 1330d4ae840..86e6c26e570 100644
--- a/app/assets/javascripts/boards/components/board_list.js
+++ b/app/assets/javascripts/boards/components/board_list.js
@@ -1,7 +1,7 @@
/* eslint-disable comma-dangle, space-before-function-paren, max-len */
-/* global Vue */
/* global Sortable */
+import Vue from 'vue';
import boardNewIssue from './board_new_issue';
import boardCard from './board_card';
@@ -48,7 +48,7 @@ import boardCard from './board_card';
this.list.getIssues(false);
}
- if (this.scrollHeight() > this.listHeight()) {
+ if (this.scrollHeight() > Math.ceil(this.listHeight())) {
this.showCount = true;
} else {
this.showCount = false;
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index dfc6eed785c..3c080008244 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -1,10 +1,11 @@
/* eslint-disable comma-dangle, space-before-function-paren, no-new */
-/* global Vue */
/* global IssuableContext */
/* global MilestoneSelect */
/* global LabelsSelect */
/* global Sidebar */
+import Vue from 'vue';
+
require('./sidebar/remove_issue');
(() => {
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.js b/app/assets/javascripts/boards/components/issue_card_inner.js
index 69e30cec4c5..ba44dc5ed94 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.js
+++ b/app/assets/javascripts/boards/components/issue_card_inner.js
@@ -1,4 +1,4 @@
-/* global Vue */
+import Vue from 'vue';
import eventHub from '../eventhub';
(() => {
diff --git a/app/assets/javascripts/boards/components/modal/empty_state.js b/app/assets/javascripts/boards/components/modal/empty_state.js
index e6973c3fd59..823319df6e7 100644
--- a/app/assets/javascripts/boards/components/modal/empty_state.js
+++ b/app/assets/javascripts/boards/components/modal/empty_state.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
const ModalStore = gl.issueBoards.ModalStore;
diff --git a/app/assets/javascripts/boards/components/modal/filters.js b/app/assets/javascripts/boards/components/modal/filters.js
index bd394a2318c..b214b5a7199 100644
--- a/app/assets/javascripts/boards/components/modal/filters.js
+++ b/app/assets/javascripts/boards/components/modal/filters.js
@@ -14,8 +14,10 @@ export default {
this.filteredSearch = new FilteredSearchBoards(this.store);
this.filteredSearch.removeTokens();
+ this.filteredSearch.handleInputPlaceholder();
+ this.filteredSearch.toggleClearSearchButton();
},
- beforeDestroy() {
+ destroyed() {
this.filteredSearch.cleanup();
FilteredSearchContainer.container = document;
this.store.path = '';
diff --git a/app/assets/javascripts/boards/components/modal/footer.js b/app/assets/javascripts/boards/components/modal/footer.js
index 1cbc422c961..887ce373096 100644
--- a/app/assets/javascripts/boards/components/modal/footer.js
+++ b/app/assets/javascripts/boards/components/modal/footer.js
@@ -1,7 +1,8 @@
/* eslint-disable no-new */
-/* global Vue */
/* global Flash */
+import Vue from 'vue';
+
require('./lists_dropdown');
(() => {
diff --git a/app/assets/javascripts/boards/components/modal/index.js b/app/assets/javascripts/boards/components/modal/index.js
index 4240c97617d..91c08cde13a 100644
--- a/app/assets/javascripts/boards/components/modal/index.js
+++ b/app/assets/javascripts/boards/components/modal/index.js
@@ -1,5 +1,6 @@
-/* global Vue */
/* global ListIssue */
+
+import Vue from 'vue';
import queryData from '../../utils/query_data';
require('./header');
@@ -64,8 +65,15 @@ require('./empty_state');
},
filter: {
handler() {
- this.page = 1;
- this.loadIssues(true);
+ if (this.$el.tagName) {
+ this.page = 1;
+ this.filterLoading = true;
+
+ this.loadIssues(true)
+ .then(() => {
+ this.filterLoading = false;
+ });
+ }
},
deep: true,
},
@@ -139,14 +147,14 @@ require('./empty_state');
:image="blankStateImage"
:issue-link-base="issueLinkBase"
:root-path="rootPath"
- v-if="!loading && showList"></modal-list>
+ v-if="!loading && showList && !filterLoading"></modal-list>
<empty-state
v-if="showEmptyState"
:image="blankStateImage"
:new-issue-path="newIssuePath"></empty-state>
<section
class="add-issues-list text-center"
- v-if="loading">
+ v-if="loading || filterLoading">
<div class="add-issues-list-loading">
<i class="fa fa-spinner fa-spin"></i>
</div>
diff --git a/app/assets/javascripts/boards/components/modal/list.js b/app/assets/javascripts/boards/components/modal/list.js
index 3730c1ecaeb..aba56d4aa31 100644
--- a/app/assets/javascripts/boards/components/modal/list.js
+++ b/app/assets/javascripts/boards/components/modal/list.js
@@ -1,6 +1,8 @@
-/* global Vue */
/* global ListIssue */
/* global bp */
+
+import Vue from 'vue';
+
(() => {
const ModalStore = gl.issueBoards.ModalStore;
diff --git a/app/assets/javascripts/boards/components/modal/lists_dropdown.js b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
index 3c05120a2da..9e9ed46ab8d 100644
--- a/app/assets/javascripts/boards/components/modal/lists_dropdown.js
+++ b/app/assets/javascripts/boards/components/modal/lists_dropdown.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
const ModalStore = gl.issueBoards.ModalStore;
diff --git a/app/assets/javascripts/boards/components/modal/tabs.js b/app/assets/javascripts/boards/components/modal/tabs.js
index 1cd6ca0ee88..23cb1b13d11 100644
--- a/app/assets/javascripts/boards/components/modal/tabs.js
+++ b/app/assets/javascripts/boards/components/modal/tabs.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
const ModalStore = gl.issueBoards.ModalStore;
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.js b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
index e74935e1cb0..d8322b34d44 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.js
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.js
@@ -1,6 +1,8 @@
/* eslint-disable no-new */
-/* global Vue */
/* global Flash */
+
+import Vue from 'vue';
+
(() => {
const Store = gl.issueBoards.BoardsStore;
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 101732309ea..1264280284c 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -28,6 +28,8 @@ export default class FilteredSearchBoards extends gl.FilteredSearchManager {
[].forEach.call(tokens, (el) => {
el.parentNode.removeChild(el);
});
+
+ this.filteredSearchInput.value = '';
}
updateTokens() {
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js b/app/assets/javascripts/boards/filters/due_date_filters.js
index 03425bb145b..70132dbfa6f 100644
--- a/app/assets/javascripts/boards/filters/due_date_filters.js
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js
@@ -1,6 +1,7 @@
-/* global Vue */
/* global dateFormat */
+import Vue from 'vue';
+
Vue.filter('due-date', (value) => {
const date = new Date(value);
return dateFormat(date, 'mmm d, yyyy', true);
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index ca5e6fa7e9d..d6175069e37 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -1,9 +1,10 @@
/* eslint-disable no-unused-vars, space-before-function-paren, arrow-body-style, arrow-parens, comma-dangle, max-len */
-/* global Vue */
/* global ListLabel */
/* global ListMilestone */
/* global ListUser */
+import Vue from 'vue';
+
class ListIssue {
constructor (obj) {
this.globalId = obj.id;
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index e54102814d6..db9bced2f89 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -1,5 +1,6 @@
/* eslint-disable space-before-function-paren, comma-dangle, no-param-reassign, camelcase, max-len, no-unused-vars */
-/* global Vue */
+
+import Vue from 'vue';
class BoardService {
constructor (root, bulkUpdatePath, boardId) {
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index 28ecb322df7..8912f234aa6 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -1,7 +1,8 @@
/* eslint-disable comma-dangle, space-before-function-paren, one-var, no-shadow, dot-notation, max-len */
-/* global Cookies */
/* global List */
+import Cookies from 'js-cookie';
+
(() => {
window.gl = window.gl || {};
window.gl.issueBoards = window.gl.issueBoards || {};
diff --git a/app/assets/javascripts/boards/stores/modal_store.js b/app/assets/javascripts/boards/stores/modal_store.js
index 7ee266a831f..9b009483a3c 100644
--- a/app/assets/javascripts/boards/stores/modal_store.js
+++ b/app/assets/javascripts/boards/stores/modal_store.js
@@ -15,6 +15,7 @@
searchTerm: '',
loading: false,
loadingNewPage: false,
+ filterLoading: false,
page: 1,
perPage: 50,
filter: {
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
index a9f2d462c31..a92e068ca5a 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_bundle.js
@@ -1,8 +1,10 @@
/* eslint-disable no-param-reassign */
+
+import Vue from 'vue';
+import VueResource from 'vue-resource';
import CommitPipelinesTable from './pipelines_table';
-window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
+Vue.use(VueResource);
/**
* Commits View > Pipelines Tab > Pipelines Table.
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table.js b/app/assets/javascripts/commit/pipelines/pipelines_table.js
index 832c4b1bd2a..a20e5bc3b1b 100644
--- a/app/assets/javascripts/commit/pipelines/pipelines_table.js
+++ b/app/assets/javascripts/commit/pipelines/pipelines_table.js
@@ -1,10 +1,10 @@
-/* eslint-disable no-new*/
-/* global Flash */
import Vue from 'vue';
import PipelinesTableComponent from '../../vue_shared/components/pipelines_table';
import PipelinesService from '../../vue_pipelines_index/services/pipelines_service';
import PipelineStore from '../../vue_pipelines_index/stores/pipelines_store';
import eventHub from '../../vue_pipelines_index/event_hub';
+import EmptyState from '../../vue_pipelines_index/components/empty_state';
+import ErrorState from '../../vue_pipelines_index/components/error_state';
import '../../lib/utils/common_utils';
import '../../vue_shared/vue_resource_interceptor';
@@ -22,6 +22,8 @@ import '../../vue_shared/vue_resource_interceptor';
export default Vue.component('pipelines-table', {
components: {
'pipelines-table-component': PipelinesTableComponent,
+ 'error-state': ErrorState,
+ 'empty-state': EmptyState,
},
/**
@@ -36,12 +38,24 @@ export default Vue.component('pipelines-table', {
return {
endpoint: pipelinesTableData.endpoint,
+ helpPagePath: pipelinesTableData.helpPagePath,
store,
state: store.state,
isLoading: false,
+ hasError: false,
};
},
+ computed: {
+ shouldRenderErrorState() {
+ return this.hasError && !this.isLoading;
+ },
+
+ shouldRenderEmptyState() {
+ return !this.state.pipelines.length && !this.isLoading;
+ },
+ },
+
/**
* When the component is about to be mounted, tell the service to fetch the data
*
@@ -80,26 +94,25 @@ export default Vue.component('pipelines-table', {
this.isLoading = false;
})
.catch(() => {
+ this.hasError = true;
this.isLoading = false;
- new Flash('An error occurred while fetching the pipelines, please reload the page again.');
});
},
},
template: `
- <div class="pipelines">
+ <div class="content-list pipelines">
<div class="realtime-loading" v-if="isLoading">
<i class="fa fa-spinner fa-spin"></i>
</div>
- <div class="blank-state blank-state-no-icon"
- v-if="!isLoading && state.pipelines.length === 0">
- <h2 class="blank-state-title js-blank-state-title">
- No pipelines to show
- </h2>
- </div>
+ <empty-state
+ v-if="shouldRenderEmptyState"
+ :help-page-path="helpPagePath" />
+
+ <error-state v-if="shouldRenderErrorState" />
- <div class="table-holder pipelines"
+ <div class="table-holder"
v-if="!isLoading && state.pipelines.length > 0">
<pipelines-table-component
:pipelines="state.pipelines"
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index fbd0db64ca7..3253eebd9b5 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -1,9 +1,11 @@
// ECMAScript polyfills
import 'core-js/fn/array/find';
+import 'core-js/fn/array/from';
import 'core-js/fn/object/assign';
import 'core-js/fn/promise';
import 'core-js/fn/string/code-point-at';
import 'core-js/fn/string/from-code-point';
+import 'core-js/fn/symbol';
// Browser polyfills
import './polyfills/custom_event';
diff --git a/app/assets/javascripts/cycle_analytics/components/limit_warning_component.js b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.js
new file mode 100644
index 00000000000..abe48572347
--- /dev/null
+++ b/app/assets/javascripts/cycle_analytics/components/limit_warning_component.js
@@ -0,0 +1,17 @@
+export default {
+ props: {
+ count: {
+ type: Number,
+ required: true,
+ },
+ },
+ template: `
+ <span v-if="count === 50" class="events-info pull-right">
+ <i class="fa fa-warning has-tooltip"
+ aria-hidden="true"
+ title="Limited to showing 50 events at most"
+ data-placement="top"></i>
+ Showing 50 events
+ </span>
+ `,
+};
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js
index b83a4c63fad..3f419a96ff9 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
@@ -13,6 +14,7 @@
<div>
<div class="events-description">
{{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
index cb1687dcc7a..7ffa38edd9e 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
@@ -13,6 +14,7 @@
<div>
<div class="events-description">
{{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
index 42e1bbce744..d736c8b0c28 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js
@@ -19,12 +19,7 @@ import iconCommit from '../svg/icon_commit.svg';
<div>
<div class="events-description">
{{ stage.description }}
- <span v-if="items.length === 50" class="events-info pull-right">
- <i class="fa fa-warning has-tooltip"
- title="Limited to showing 50 events at most"
- data-placement="top"></i>
- Showing 50 events
- </span>
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item">
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js
index 73f4205b578..698a79ca68c 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
@@ -13,6 +14,7 @@
<div>
<div class="events-description">
{{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js
index 501ffb1fac9..e63c41f2a57 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
@@ -13,6 +14,7 @@
<div>
<div class="events-description">
{{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
index 8fa63734cf1..d51f7134e25 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js
@@ -17,6 +17,7 @@ import iconBranch from '../svg/icon_branch.svg';
<div>
<div class="events-description">
{{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component">
diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js
index 0015249cfaa..17ae3a9ddc1 100644
--- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js
@@ -18,6 +18,7 @@ import iconBranch from '../svg/icon_branch.svg';
<div>
<div class="events-description">
{{ stage.description }}
+ <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component">
diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js b/app/assets/javascripts/cycle_analytics/components/total_time_component.js
index 0d85e1a4678..b4442ea5566 100644
--- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js
+++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
index beff293b587..b099b39e58f 100644
--- a/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
+++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_bundle.js
@@ -1,9 +1,9 @@
-/* global Vue */
-/* global Cookies */
/* global Flash */
-window.Vue = require('vue');
-window.Cookies = require('js-cookie');
+import Vue from 'vue';
+import Cookies from 'js-cookie';
+import LimitWarningComponent from './components/limit_warning_component';
+
require('./components/stage_code_component');
require('./components/stage_issue_component');
require('./components/stage_plan_component');
@@ -131,5 +131,6 @@ $(() => {
});
// Register global components
+ Vue.component('limit-warning', LimitWarningComponent);
Vue.component('total-time', gl.cycleAnalytics.TotalTimeComponent);
});
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index cfa60325fcc..88180149715 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -33,11 +33,7 @@ class Diff {
handleClickUnfold(e) {
const $target = $(e.target);
- // current babel config relies on iterators implementation, so we cannot simply do:
- // const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
- const ref = this.lineNumbers($target.parent());
- const oldLineNumber = ref[0];
- const newLineNumber = ref[1];
+ const [oldLineNumber, newLineNumber] = this.lineNumbers($target.parent());
const offset = newLineNumber - oldLineNumber;
const bottom = $target.hasClass('js-unfold-bottom');
let since;
@@ -105,10 +101,11 @@ class Diff {
}
lineNumbers(line) {
- if (!line.children().length) {
+ const children = line.find('.diff-line-num').toArray();
+ if (children.length !== 2) {
return [0, 0];
}
- return line.find('.diff-line-num').map((i, elm) => parseInt($(elm).data('linenumber'), 10));
+ return children.map(elm => parseInt($(elm).data('linenumber'), 10) || 0);
}
highlightSelectedLine() {
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 d948dff58ec..fc2f20e3bcb 100644
--- a/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/comment_resolve_btn.js
@@ -1,6 +1,7 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, quotes, no-lonely-if, max-len */
/* global CommentsStore */
-const Vue = require('vue');
+
+import Vue from 'vue';
(() => {
const CommentAndResolveBtn = Vue.extend({
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 dd7081aefb7..0297add94d5 100644
--- a/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
+++ b/app/assets/javascripts/diff_notes/components/diff_note_avatars.js
@@ -1,4 +1,6 @@
-/* global CommentsStore Cookies notes */
+/* global CommentsStore */
+/* global notes */
+
import Vue from 'vue';
import collapseIcon from '../icons/collapse_icon.svg';
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 283dc330cad..8edc45130fc 100644
--- a/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/jump_to_discussion.js
@@ -1,7 +1,8 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, no-else-return, guard-for-in, no-restricted-syntax, one-var, space-before-function-paren, no-lonely-if, no-continue, brace-style, max-len, quotes */
/* global DiscussionMixins */
/* global CommentsStore */
-const Vue = require('vue');
+
+import Vue from 'vue';
(() => {
const JumpToDiscussion = Vue.extend({
diff --git a/app/assets/javascripts/diff_notes/components/new_issue_for_discussion.js b/app/assets/javascripts/diff_notes/components/new_issue_for_discussion.js
index e86bef47172..8eb0e10b832 100644
--- a/app/assets/javascripts/diff_notes/components/new_issue_for_discussion.js
+++ b/app/assets/javascripts/diff_notes/components/new_issue_for_discussion.js
@@ -1,6 +1,7 @@
-/* global Vue */
/* global CommentsStore */
+import Vue from 'vue';
+
(() => {
const NewIssueForDiscussion = Vue.extend({
props: {
diff --git a/app/assets/javascripts/diff_notes/components/resolve_btn.js b/app/assets/javascripts/diff_notes/components/resolve_btn.js
index fbd980f0fce..312f38ce241 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_btn.js
@@ -2,7 +2,8 @@
/* global CommentsStore */
/* global ResolveService */
/* global Flash */
-const Vue = require('vue');
+
+import Vue from 'vue';
(() => {
const ResolveBtn = Vue.extend({
diff --git a/app/assets/javascripts/diff_notes/components/resolve_count.js b/app/assets/javascripts/diff_notes/components/resolve_count.js
index de9367f2136..27147ac6b5c 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_count.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_count.js
@@ -1,7 +1,8 @@
/* eslint-disable comma-dangle, object-shorthand, func-names, no-param-reassign */
/* global DiscussionMixins */
/* global CommentsStore */
-const Vue = require('vue');
+
+import Vue from 'vue';
((w) => {
w.ResolveCount = Vue.extend({
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 7c5fcd04d2d..a964b7d0c6b 100644
--- a/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
+++ b/app/assets/javascripts/diff_notes/components/resolve_discussion_btn.js
@@ -2,7 +2,7 @@
/* global CommentsStore */
/* global ResolveService */
-const Vue = require('vue');
+import Vue from 'vue';
(() => {
const ResolveDiscussionBtn = Vue.extend({
diff --git a/app/assets/javascripts/diff_notes/diff_notes_bundle.js b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
index 4f6b86a917c..b6b47e2da6f 100644
--- a/app/assets/javascripts/diff_notes/diff_notes_bundle.js
+++ b/app/assets/javascripts/diff_notes/diff_notes_bundle.js
@@ -1,8 +1,8 @@
/* eslint-disable func-names, comma-dangle, new-cap, no-new, max-len */
-/* global Vue */
/* global ResolveCount */
-const Vue = require('vue');
+import Vue from 'vue';
+
require('./models/discussion');
require('./models/note');
require('./stores/comments');
diff --git a/app/assets/javascripts/diff_notes/models/discussion.js b/app/assets/javascripts/diff_notes/models/discussion.js
index dce1a9b58bd..dc43e4b2cc7 100644
--- a/app/assets/javascripts/diff_notes/models/discussion.js
+++ b/app/assets/javascripts/diff_notes/models/discussion.js
@@ -1,7 +1,8 @@
/* eslint-disable space-before-function-paren, camelcase, guard-for-in, no-restricted-syntax, no-unused-vars, max-len */
-/* global Vue */
/* global NoteModel */
+import Vue from 'vue';
+
class DiscussionModel {
constructor (discussionId) {
this.id = discussionId;
diff --git a/app/assets/javascripts/diff_notes/services/resolve.js b/app/assets/javascripts/diff_notes/services/resolve.js
index 090c454e9e4..bfa4fc9037a 100644
--- a/app/assets/javascripts/diff_notes/services/resolve.js
+++ b/app/assets/javascripts/diff_notes/services/resolve.js
@@ -2,10 +2,13 @@
/* global Flash */
/* global CommentsStore */
-const Vue = window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+
require('../../vue_shared/vue_resource_interceptor');
+Vue.use(VueResource);
+
(() => {
window.gl = window.gl || {};
diff --git a/app/assets/javascripts/diff_notes/stores/comments.js b/app/assets/javascripts/diff_notes/stores/comments.js
index 69c4d7a8434..e6cbda56c91 100644
--- a/app/assets/javascripts/diff_notes/stores/comments.js
+++ b/app/assets/javascripts/diff_notes/stores/comments.js
@@ -1,7 +1,8 @@
/* eslint-disable object-shorthand, func-names, camelcase, no-restricted-syntax, guard-for-in, comma-dangle, max-len, no-param-reassign */
-/* global Vue */
/* global DiscussionModel */
+import Vue from 'vue';
+
((w) => {
w.CommentsStore = {
state: {},
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 3557f6f617e..d1a662459e1 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -41,9 +41,9 @@ import GroupsList from './groups_list';
import ProjectsList from './projects_list';
import MiniPipelineGraph from './mini_pipeline_graph_dropdown';
import BlobLinePermalinkUpdater from './blob/blob_line_permalink_updater';
+import UserCallout from './user_callout';
const ShortcutsBlob = require('./shortcuts_blob');
-const UserCallout = require('./user_callout');
(function() {
var Dispatcher;
diff --git a/app/assets/javascripts/droplab/droplab_filter.js b/app/assets/javascripts/droplab/droplab_filter.js
index 9b40a3f20a4..7f7d93f3e27 100644
--- a/app/assets/javascripts/droplab/droplab_filter.js
+++ b/app/assets/javascripts/droplab/droplab_filter.js
@@ -56,10 +56,12 @@ require('../window')(function(w){
this.hookInput = hookInput;
this.hookInput.trigger.addEventListener('keyup.dl', this.keydownWrapper);
+ this.hookInput.trigger.addEventListener('mousedown.dl', this.keydownWrapper);
},
destroy: function destroy(){
this.hookInput.trigger.removeEventListener('keyup.dl', this.keydownWrapper);
+ this.hookInput.trigger.removeEventListener('mousedown.dl', this.keydownWrapper);
}
};
});
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index c6bb7fda8f2..22352950452 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -384,7 +384,7 @@ import FilteredSearchContainer from './container';
paths.push(`search=${sanitized}`);
}
- const parameterizedUrl = `?scope=all&utf8=✓&${paths.join('&')}`;
+ const parameterizedUrl = `?scope=all&utf8=%E2%9C%93&${paths.join('&')}`;
if (this.updateObject) {
this.updateObject(parameterizedUrl);
diff --git a/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js b/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js
index 357b3487ca9..aec13e78f42 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/collapsed_state.js
@@ -1,4 +1,4 @@
-/* global Vue */
+import Vue from 'vue';
import stopwatchSvg from 'icons/_icon_stopwatch.svg';
require('../../../lib/utils/pretty_time');
diff --git a/app/assets/javascripts/issuable/time_tracking/components/comparison_pane.js b/app/assets/javascripts/issuable/time_tracking/components/comparison_pane.js
index 750468c679b..c55e263f6f4 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/comparison_pane.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/comparison_pane.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
require('../../../lib/utils/pretty_time');
(() => {
diff --git a/app/assets/javascripts/issuable/time_tracking/components/estimate_only_pane.js b/app/assets/javascripts/issuable/time_tracking/components/estimate_only_pane.js
index 309e9f2f9ef..a7fbd704c40 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/estimate_only_pane.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/estimate_only_pane.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
Vue.component('time-tracking-estimate-only-pane', {
name: 'time-tracking-estimate-only-pane',
diff --git a/app/assets/javascripts/issuable/time_tracking/components/help_state.js b/app/assets/javascripts/issuable/time_tracking/components/help_state.js
index d7ced6d7151..344b29ebea4 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/help_state.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/help_state.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
Vue.component('time-tracking-help-state', {
name: 'time-tracking-help-state',
diff --git a/app/assets/javascripts/issuable/time_tracking/components/no_tracking_pane.js b/app/assets/javascripts/issuable/time_tracking/components/no_tracking_pane.js
index 1d2ca643b5b..b081adf5e64 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/no_tracking_pane.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/no_tracking_pane.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
Vue.component('time-tracking-no-tracking-pane', {
name: 'time-tracking-no-tracking-pane',
diff --git a/app/assets/javascripts/issuable/time_tracking/components/spent_only_pane.js b/app/assets/javascripts/issuable/time_tracking/components/spent_only_pane.js
index ed283fec3c3..edb9169112f 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/spent_only_pane.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/spent_only_pane.js
@@ -1,4 +1,5 @@
-/* global Vue */
+import Vue from 'vue';
+
(() => {
Vue.component('time-tracking-spent-only-pane', {
name: 'time-tracking-spent-only-pane',
diff --git a/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js b/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js
index 1fae2d62b14..0213522f551 100644
--- a/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js
+++ b/app/assets/javascripts/issuable/time_tracking/components/time_tracker.js
@@ -1,4 +1,4 @@
-/* global Vue */
+import Vue from 'vue';
require('./help_state');
require('./collapsed_state');
diff --git a/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js b/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js
index 0134b7cb6f3..1689a69e1ed 100644
--- a/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js
+++ b/app/assets/javascripts/issuable/time_tracking/time_tracking_bundle.js
@@ -1,11 +1,12 @@
-/* global Vue */
+import Vue from 'vue';
+import VueResource from 'vue-resource';
-window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
require('./components/time_tracker');
require('../../smart_interval');
require('../../subbable_resource');
+Vue.use(VueResource);
+
(() => {
/* This Vue instance represents what will become the parent instance for the
* sidebar. It will be responsible for managing `issuable` state and propagating
diff --git a/app/assets/javascripts/issuable_context.js b/app/assets/javascripts/issuable_context.js
index 115312d4b83..834b98e8601 100644
--- a/app/assets/javascripts/issuable_context.js
+++ b/app/assets/javascripts/issuable_context.js
@@ -1,8 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-new, comma-dangle, quotes, prefer-arrow-callback, consistent-return, one-var, no-var, one-var-declaration-per-line, no-underscore-dangle, max-len */
/* global UsersSelect */
-/* global Cookies */
/* global bp */
+import Cookies from 'js-cookie';
+
(function() {
this.IssuableContext = (function() {
function IssuableContext(currentUser) {
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
new file mode 100644
index 00000000000..c30a1fcb5da
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -0,0 +1,73 @@
+import httpStatusCodes from './http_status';
+
+/**
+ * Polling utility for handling realtime updates.
+ * Service for vue resouce and method need to be provided as props
+ *
+ * @example
+ * new poll({
+ * resource: resource,
+ * method: 'name',
+ * data: {page: 1, scope: 'all'},
+ * successCallback: () => {},
+ * errorCallback: () => {},
+ * }).makeRequest();
+ *
+ * this.service = new BoardsService(endpoint);
+ * new poll({
+ * resource: this.service,
+ * method: 'get',
+ * data: {page: 1, scope: 'all'},
+ * successCallback: () => {},
+ * errorCallback: () => {},
+ * }).makeRequest();
+ *
+ *
+ * 1. Checks for response and headers before start polling
+ * 2. Interval is provided by `Poll-Interval` header.
+ * 3. If `Poll-Interval` is -1, we stop polling
+ * 4. If HTTP response is 200, we poll.
+ * 5. If HTTP response is different from 200, we stop polling.
+ *
+ */
+export default class Poll {
+ constructor(options = {}) {
+ this.options = options;
+ this.options.data = options.data || {};
+
+ this.intervalHeader = 'POLL-INTERVAL';
+ this.timeoutID = null;
+ this.canPoll = true;
+ }
+
+ checkConditions(response) {
+ const headers = gl.utils.normalizeHeaders(response.headers);
+ const pollInterval = headers[this.intervalHeader];
+
+ if (pollInterval > 0 && response.status === httpStatusCodes.OK && this.canPoll) {
+ this.timeoutID = setTimeout(() => {
+ this.makeRequest();
+ }, pollInterval);
+ }
+
+ this.options.successCallback(response);
+ }
+
+ makeRequest() {
+ const { resource, method, data, errorCallback } = this.options;
+
+ return resource[method](data)
+ .then(response => this.checkConditions(response))
+ .catch(error => errorCallback(error));
+ }
+
+ /**
+ * Stops the polling recursive chain
+ * and guarantees if the timeout is already running it won't make another request by
+ * cancelling the previously established timeout.
+ */
+ stop() {
+ this.canPoll = false;
+ clearTimeout(this.timeoutID);
+ }
+}
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 81d5748191d..9007d661d01 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -1,6 +1,5 @@
/* eslint-disable func-names, space-before-function-paren, no-var, quotes, consistent-return, prefer-arrow-callback, comma-dangle, object-shorthand, no-new, max-len, no-multi-spaces, import/newline-after-import, import/first */
/* global bp */
-/* global Cookies */
/* global Flash */
/* global ConfirmDangerModal */
/* global Aside */
@@ -24,7 +23,6 @@ import './extensions/array';
window.jQuery = jQuery;
window.$ = jQuery;
window._ = _;
-window.Cookies = Cookies;
window.Pikaday = Pikaday;
window.Dropzone = Dropzone;
window.Sortable = Sortable;
@@ -49,15 +47,6 @@ import { installGlEmojiElement } from './behaviors/gl_emoji';
installGlEmojiElement();
// blob
-import './blob/blob_ci_yaml';
-import './blob/blob_dockerfile_selector';
-import './blob/blob_dockerfile_selectors';
-import './blob/blob_file_dropzone';
-import './blob/blob_gitignore_selector';
-import './blob/blob_gitignore_selectors';
-import './blob/blob_license_selector';
-import './blob/blob_license_selectors';
-import './blob/template_selector';
import './blob/create_branch_dropdown';
import './blob/target_branch_dropdown';
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 c7e78fed8fe..645045fea88 100644
--- a/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
+++ b/app/assets/javascripts/merge_conflicts/components/diff_file_editor.js
@@ -1,8 +1,9 @@
/* eslint-disable comma-dangle, quote-props, no-useless-computed-key, object-shorthand, no-new, no-param-reassign, max-len */
-/* global Vue */
/* global ace */
/* global Flash */
+import Vue from 'vue';
+
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
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 240c8f98932..56d6678e1bd 100644
--- a/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js
+++ b/app/assets/javascripts/merge_conflicts/components/inline_conflict_lines.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign, comma-dangle */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
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 97753c50b60..0fc4a13450a 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
@@ -1,5 +1,6 @@
/* eslint-disable no-param-reassign, comma-dangle */
-/* global Vue */
+
+import Vue from 'vue';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
index 74587df22c5..c4e379a4a0b 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflict_store.js
@@ -1,6 +1,7 @@
/* eslint-disable comma-dangle, object-shorthand, no-param-reassign, camelcase, no-nested-ternary, no-continue, max-len */
-/* global Cookies */
-/* global Vue */
+
+import Vue from 'vue';
+import Cookies from 'js-cookie';
((global) => {
global.mergeConflicts = global.mergeConflicts || {};
diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
index 653e52fb6bf..15992460146 100644
--- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
+++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js
@@ -1,8 +1,8 @@
/* eslint-disable new-cap, comma-dangle, no-new */
-/* global Vue */
/* global Flash */
-window.Vue = require('vue');
+import Vue from 'vue';
+
require('./merge_conflict_store');
require('./merge_conflict_service');
require('./mixins/line_conflict_utils');
diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js
index 190336dbd20..811f90c5a87 100644
--- a/app/assets/javascripts/merge_request_tabs.js
+++ b/app/assets/javascripts/merge_request_tabs.js
@@ -1,10 +1,10 @@
/* eslint-disable no-new, class-methods-use-this */
/* global Breakpoints */
-/* global Cookies */
/* global Flash */
+import Cookies from 'js-cookie';
+
require('./breakpoints');
-window.Cookies = require('js-cookie');
require('./flash');
/* eslint-disable max-len */
@@ -127,9 +127,6 @@ require('./flash');
if (this.diffViewType() === 'parallel') {
this.expandViewContainer();
}
- $.scrollTo('.merge-request-details .merge-request-tabs', {
- offset: 0,
- });
} else if (action === 'pipelines') {
if (this.pipelinesLoaded) {
return;
diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js
index 40e977df693..ac4fad88fe5 100644
--- a/app/assets/javascripts/milestone_select.js
+++ b/app/assets/javascripts/milestone_select.js
@@ -1,8 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-underscore-dangle, prefer-arrow-callback, max-len, one-var, one-var-declaration-per-line, no-unused-vars, object-shorthand, comma-dangle, no-else-return, no-self-compare, consistent-return, no-param-reassign, no-shadow */
-/* global Vue */
/* global Issuable */
/* global ListMilestone */
+import Vue from 'vue';
+
(function() {
this.MilestoneSelect = (function() {
function MilestoneSelect(currentProject, els) {
diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js
index fcffc11a2df..844a0785bc9 100644
--- a/app/assets/javascripts/monitoring/prometheus_graph.js
+++ b/app/assets/javascripts/monitoring/prometheus_graph.js
@@ -1,4 +1,4 @@
-/* eslint-disable no-new */
+/* eslint-disable no-new*/
/* global Flash */
import d3 from 'd3';
@@ -180,7 +180,7 @@ class PrometheusGraph {
// Metric Usage
axisLabelContainer.append('rect')
.attr('x', this.originalWidth - 170)
- .attr('y', (this.originalHeight / 2) - 80)
+ .attr('y', (this.originalHeight / 2) - 60)
.style('fill', graphSpecifics.area_fill_color)
.attr('width', 20)
.attr('height', 35);
@@ -188,13 +188,13 @@ class PrometheusGraph {
axisLabelContainer.append('text')
.attr('class', 'label-axis-text')
.attr('x', this.originalWidth - 140)
- .attr('y', (this.originalHeight / 2) - 65)
- .text(graphSpecifics.graph_legend_title);
+ .attr('y', (this.originalHeight / 2) - 50)
+ .text('Average');
axisLabelContainer.append('text')
.attr('class', 'text-metric-usage')
.attr('x', this.originalWidth - 140)
- .attr('y', (this.originalHeight / 2) - 50);
+ .attr('y', (this.originalHeight / 2) - 25);
}
handleMouseOverGraph(x, y, valuesToPlot, chart, prometheusGraphContainer, key) {
@@ -263,12 +263,12 @@ class PrometheusGraph {
cpu_values: {
area_fill_color: '#edf3fc',
line_color: '#5b99f7',
- graph_legend_title: 'CPU Usage (Cores)',
+ graph_legend_title: 'CPU utilization (%)',
},
memory_values: {
area_fill_color: '#fca326',
line_color: '#fc6d26',
- graph_legend_title: 'Memory Usage (MB)',
+ graph_legend_title: 'Memory usage (MB)',
},
};
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index 47cc34e7a20..1d563c63f39 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -1,14 +1,14 @@
/* eslint-disable no-restricted-properties, func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-use-before-define, camelcase, no-unused-expressions, quotes, max-len, one-var, one-var-declaration-per-line, default-case, prefer-template, consistent-return, no-alert, no-return-assign, no-param-reassign, prefer-arrow-callback, no-else-return, comma-dangle, no-new, brace-style, no-lonely-if, vars-on-top, no-unused-vars, no-sequences, no-shadow, newline-per-chained-call, no-useless-escape */
/* global Flash */
/* global Autosave */
-/* global Cookies */
/* global ResolveService */
/* global mrRefreshWidgetUrl */
+import Cookies from 'js-cookie';
+
require('./autosave');
window.autosize = require('vendor/autosize');
window.Dropzone = require('dropzone');
-window.Cookies = require('js-cookie');
require('./dropzone_input');
require('./gfm_auto_complete');
require('vendor/jquery.caret'); // required by jquery.atwho
diff --git a/app/assets/javascripts/project.js b/app/assets/javascripts/project.js
index db7ceaa2421..f944fcc5a58 100644
--- a/app/assets/javascripts/project.js
+++ b/app/assets/javascripts/project.js
@@ -1,7 +1,8 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, quotes, consistent-return, no-new, prefer-arrow-callback, no-return-assign, one-var, one-var-declaration-per-line, object-shorthand, comma-dangle, no-else-return, newline-per-chained-call, no-shadow, vars-on-top, prefer-template, max-len */
-/* global Cookies */
/* global ProjectSelect */
+import Cookies from 'js-cookie';
+
(function() {
this.Project = (function() {
function Project() {
diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js
index 48cae8a4fa9..ea91aaa10a6 100644
--- a/app/assets/javascripts/render_gfm.js
+++ b/app/assets/javascripts/render_gfm.js
@@ -1,4 +1,5 @@
-/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, max-len */
+/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-else-return, prefer-arrow-callback, max-len */
+
// Render Gitlab flavoured Markdown
//
// Delegates to syntax highlight and render math
diff --git a/app/assets/javascripts/render_math.js b/app/assets/javascripts/render_math.js
index 76c61c001ba..8b3fee49cb9 100644
--- a/app/assets/javascripts/render_math.js
+++ b/app/assets/javascripts/render_math.js
@@ -1,4 +1,6 @@
-/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, max-len, no-console */
+/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-else-return, prefer-arrow-callback, max-len, no-console */
+/* global katex */
+
// Renders math using KaTeX in any element with the
// `js-render-math` class
//
diff --git a/app/assets/javascripts/right_sidebar.js b/app/assets/javascripts/right_sidebar.js
index 903862cac6b..64a68d56962 100644
--- a/app/assets/javascripts/right_sidebar.js
+++ b/app/assets/javascripts/right_sidebar.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, no-unused-vars, consistent-return, one-var, one-var-declaration-per-line, quotes, prefer-template, object-shorthand, comma-dangle, no-else-return, no-param-reassign, max-len */
-/* global Cookies */
+
+import Cookies from 'js-cookie';
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; };
@@ -199,7 +200,7 @@
Sidebar.prototype.setSidebarHeight = function() {
const $navHeight = $('.navbar-gitlab').outerHeight() + $('.layout-nav').outerHeight();
const $rightSidebar = $('.js-right-sidebar');
- const diff = $navHeight - $('body').scrollTop();
+ const diff = $navHeight - $(window).scrollTop();
if (diff > 0) {
$rightSidebar.outerHeight($(window).height() - diff);
} else {
diff --git a/app/assets/javascripts/shortcuts.js b/app/assets/javascripts/shortcuts.js
index 81766f4bd55..fd5097696ad 100644
--- a/app/assets/javascripts/shortcuts.js
+++ b/app/assets/javascripts/shortcuts.js
@@ -33,6 +33,10 @@
};
Shortcuts.prototype.toggleMarkdownPreview = function(e) {
+ // Check if short-cut was triggered while in Write Mode
+ if ($(e.target).hasClass('js-note-text')) {
+ $('.js-md-preview-button').focus();
+ }
return $(document).triggerHandler('markdown-preview:toggle', [e]);
};
diff --git a/app/assets/javascripts/shortcuts_dashboard_navigation.js b/app/assets/javascripts/shortcuts_dashboard_navigation.js
index e7baea894f6..4f1a19924a4 100644
--- a/app/assets/javascripts/shortcuts_dashboard_navigation.js
+++ b/app/assets/javascripts/shortcuts_dashboard_navigation.js
@@ -22,6 +22,9 @@ require('./shortcuts');
Mousetrap.bind('g m', function() {
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-merge_requests');
});
+ Mousetrap.bind('g t', function() {
+ return ShortcutsDashboardNavigation.findAndFollowLink('.shortcuts-todos');
+ });
Mousetrap.bind('g p', function() {
return ShortcutsDashboardNavigation.findAndFollowLink('.dashboard-shortcuts-projects');
});
diff --git a/app/assets/javascripts/shortcuts_navigation.js b/app/assets/javascripts/shortcuts_navigation.js
index 09a58cad2b2..3f5d6724417 100644
--- a/app/assets/javascripts/shortcuts_navigation.js
+++ b/app/assets/javascripts/shortcuts_navigation.js
@@ -43,6 +43,9 @@ require('./shortcuts');
Mousetrap.bind('g m', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-merge_requests');
});
+ Mousetrap.bind('g t', function() {
+ return ShortcutsNavigation.findAndFollowLink('.shortcuts-todos');
+ });
Mousetrap.bind('g w', function() {
return ShortcutsNavigation.findAndFollowLink('.shortcuts-wiki');
});
diff --git a/app/assets/javascripts/subscription.js b/app/assets/javascripts/subscription.js
index 62d1604fe9e..9c307915ec4 100644
--- a/app/assets/javascripts/subscription.js
+++ b/app/assets/javascripts/subscription.js
@@ -1,4 +1,4 @@
-/* global Vue */
+import Vue from 'vue';
(() => {
class Subscription {
diff --git a/app/assets/javascripts/templates/issuable_template_selector.js b/app/assets/javascripts/templates/issuable_template_selector.js
index e9e9aafd71a..32067ed1fee 100644
--- a/app/assets/javascripts/templates/issuable_template_selector.js
+++ b/app/assets/javascripts/templates/issuable_template_selector.js
@@ -1,15 +1,15 @@
/* eslint-disable comma-dangle, max-len, no-useless-return, no-param-reassign, max-len */
/* global Api */
-require('../blob/template_selector');
+import TemplateSelector from '../blob/template_selectors/template_selector';
((global) => {
- class IssuableTemplateSelector extends gl.TemplateSelector {
+ class IssuableTemplateSelector extends TemplateSelector {
constructor(...args) {
super(...args);
this.projectPath = this.dropdown.data('project-path');
this.namespacePath = this.dropdown.data('namespace-path');
- this.issuableType = this.wrapper.data('issuable-type');
+ this.issuableType = this.$dropdownContainer.data('issuable-type');
this.titleInput = $(`#${this.issuableType}_title`);
const initialQuery = {
@@ -41,16 +41,16 @@ require('../blob/template_selector');
}
setInputValueToTemplateContent() {
- // `this.requestFileSuccess` sets the value of the description input field
+ // `this.setEditorContent` sets the value of the description input field
// to the content of the template selected.
if (this.titleInput.val() === '') {
// If the title has not yet been set, focus the title input and
// skip focusing the description input by setting `true` as the
- // `skipFocus` option to `requestFileSuccess`.
- this.requestFileSuccess(this.currentTemplate, { skipFocus: true });
+ // `skipFocus` option to `setEditorContent`.
+ this.setEditorContent(this.currentTemplate, { skipFocus: true });
this.titleInput.focus();
} else {
- this.requestFileSuccess(this.currentTemplate, { skipFocus: false });
+ this.setEditorContent(this.currentTemplate, { skipFocus: false });
}
return;
}
diff --git a/app/assets/javascripts/user.js b/app/assets/javascripts/user.js
index 059e6c628b3..19c9efe7fbd 100644
--- a/app/assets/javascripts/user.js
+++ b/app/assets/javascripts/user.js
@@ -1,5 +1,6 @@
/* eslint-disable class-methods-use-this, comma-dangle, arrow-parens, no-param-reassign */
-/* global Cookies */
+
+import Cookies from 'js-cookie';
((global) => {
global.User = class {
diff --git a/app/assets/javascripts/user_callout.js b/app/assets/javascripts/user_callout.js
index 99419e85b20..b27d252a3ef 100644
--- a/app/assets/javascripts/user_callout.js
+++ b/app/assets/javascripts/user_callout.js
@@ -1,4 +1,4 @@
-/* global Cookies */
+import Cookies from 'js-cookie';
const userCalloutElementName = '.user-callout';
const closeButton = '.close-user-callout';
@@ -27,7 +27,7 @@ const USER_CALLOUT_TEMPLATE = `
</div>
</div>`;
-class UserCallout {
+export default class UserCallout {
constructor() {
this.isCalloutDismissed = Cookies.get(USER_CALLOUT_COOKIE);
this.userCalloutBody = $(userCalloutElementName);
@@ -56,5 +56,3 @@ class UserCallout {
}
}
}
-
-module.exports = UserCallout;
diff --git a/app/assets/javascripts/user_tabs.js b/app/assets/javascripts/user_tabs.js
index 465618e3d53..5db0d936ad8 100644
--- a/app/assets/javascripts/user_tabs.js
+++ b/app/assets/javascripts/user_tabs.js
@@ -1,4 +1,4 @@
-/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign */
+/* eslint-disable max-len, space-before-function-paren, no-underscore-dangle, consistent-return, comma-dangle, no-unused-vars, dot-notation, no-new, no-return-assign, camelcase, no-param-reassign, class-methods-use-this */
/*
UserTabs
@@ -82,8 +82,19 @@ content on the Users#show page.
}
bindEvents() {
- return this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
+ this.changeProjectsPageWrapper = this.changeProjectsPage.bind(this);
+
+ this.$parentEl.off('shown.bs.tab', '.nav-links a[data-toggle="tab"]')
.on('shown.bs.tab', '.nav-links a[data-toggle="tab"]', event => this.tabShown(event));
+
+ this.$parentEl.on('click', '.gl-pagination a', this.changeProjectsPageWrapper);
+ }
+
+ changeProjectsPage(e) {
+ e.preventDefault();
+
+ $('.tab-pane.active').empty();
+ this.loadTab($(e.target).attr('href'), this.getCurrentAction());
}
tabShown(event) {
@@ -119,7 +130,7 @@ content on the Users#show page.
complete: () => this.toggleLoading(false),
dataType: 'json',
type: 'GET',
- url: `${source}.json`,
+ url: source,
success: (data) => {
const tabSelector = `div#${action}`;
this.$parentEl.find(tabSelector).html(data.html);
@@ -153,6 +164,10 @@ content on the Users#show page.
}, document.title, new_state);
return new_state;
}
+
+ getCurrentAction() {
+ return this.$parentEl.find('.nav-links .active a').data('action');
+ }
}
global.UserTabs = UserTabs;
})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index eb897e9dfe9..48e20cf501f 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -1,8 +1,9 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, prefer-rest-params, wrap-iife, quotes, max-len, one-var-declaration-per-line, vars-on-top, prefer-arrow-callback, consistent-return, comma-dangle, object-shorthand, no-shadow, no-unused-vars, no-else-return, no-self-compare, prefer-template, no-unused-expressions, no-lonely-if, yoda, prefer-spread, no-void, camelcase, no-param-reassign */
-/* global Vue */
/* global Issuable */
/* global ListUser */
+import Vue from 'vue';
+
(function() {
var bind = function(fn, me) { return function() { return fn.apply(me, arguments); }; },
slice = [].slice;
diff --git a/app/assets/javascripts/vue_pipelines_index/components/empty_state.js b/app/assets/javascripts/vue_pipelines_index/components/empty_state.js
new file mode 100644
index 00000000000..56b4858f4b4
--- /dev/null
+++ b/app/assets/javascripts/vue_pipelines_index/components/empty_state.js
@@ -0,0 +1,33 @@
+import pipelinesEmptyStateSVG from 'empty_states/icons/_pipelines_empty.svg';
+
+export default {
+ props: {
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+ },
+
+ template: `
+ <div class="row empty-state">
+ <div class="col-xs-12">
+ <div class="svg-content">
+ ${pipelinesEmptyStateSVG}
+ </div>
+ </div>
+
+ <div class="col-xs-12 text-center">
+ <div class="text-content">
+ <h4>Build with confidence</h4>
+ <p>
+ Continous Integration can help catch bugs by running your tests automatically,
+ while Continuous Deployment can help you deliver code to your product environment.
+ </p>
+ <a :href="helpPagePath" class="btn btn-info">
+ Get started with Pipelines
+ </a>
+ </div>
+ </div>
+ </div>
+ `,
+};
diff --git a/app/assets/javascripts/vue_pipelines_index/components/error_state.js b/app/assets/javascripts/vue_pipelines_index/components/error_state.js
new file mode 100644
index 00000000000..e5d228bddf8
--- /dev/null
+++ b/app/assets/javascripts/vue_pipelines_index/components/error_state.js
@@ -0,0 +1,19 @@
+import pipelinesErrorStateSVG from 'empty_states/icons/_pipelines_failed.svg';
+
+export default {
+ template: `
+ <div class="row empty-state js-pipelines-error-state">
+ <div class="col-xs-12">
+ <div class="svg-content">
+ ${pipelinesErrorStateSVG}
+ </div>
+ </div>
+
+ <div class="col-xs-12 text-center">
+ <div class="text-content">
+ <h4>The API failed to fetch the pipelines.</h4>
+ </div>
+ </div>
+ </div>
+ `,
+};
diff --git a/app/assets/javascripts/vue_pipelines_index/components/nav_controls.js b/app/assets/javascripts/vue_pipelines_index/components/nav_controls.js
new file mode 100644
index 00000000000..6aa10531034
--- /dev/null
+++ b/app/assets/javascripts/vue_pipelines_index/components/nav_controls.js
@@ -0,0 +1,52 @@
+export default {
+ props: {
+ newPipelinePath: {
+ type: String,
+ required: true,
+ },
+
+ hasCiEnabled: {
+ type: Boolean,
+ required: true,
+ },
+
+ helpPagePath: {
+ type: String,
+ required: true,
+ },
+
+ ciLintPath: {
+ type: String,
+ required: true,
+ },
+
+ canCreatePipeline: {
+ type: Boolean,
+ required: true,
+ },
+ },
+
+ template: `
+ <div class="nav-controls">
+ <a
+ v-if="canCreatePipeline"
+ :href="newPipelinePath"
+ class="btn btn-create">
+ Run Pipeline
+ </a>
+
+ <a
+ v-if="!hasCiEnabled"
+ :href="helpPagePath"
+ class="btn btn-info">
+ Get started with Pipelines
+ </a>
+
+ <a
+ :href="ciLintPath"
+ class="btn btn-default">
+ CI Lint
+ </a>
+ </div>
+ `,
+};
diff --git a/app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js b/app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js
new file mode 100644
index 00000000000..b4480bd98c7
--- /dev/null
+++ b/app/assets/javascripts/vue_pipelines_index/components/navigation_tabs.js
@@ -0,0 +1,68 @@
+export default {
+ props: {
+ scope: {
+ type: String,
+ required: true,
+ },
+
+ count: {
+ type: Object,
+ required: true,
+ },
+
+ paths: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ template: `
+ <ul class="nav-links">
+ <li
+ class="js-pipelines-tab-all"
+ :class="{ 'active': scope === 'all'}">
+ <a :href="paths.allPath">
+ All
+ <span class="badge js-totalbuilds-count">
+ {{count.all}}
+ </span>
+ </a>
+ </li>
+ <li class="js-pipelines-tab-pending"
+ :class="{ 'active': scope === 'pending'}">
+ <a :href="paths.pendingPath">
+ Pending
+ <span class="badge">
+ {{count.pending}}
+ </span>
+ </a>
+ </li>
+ <li class="js-pipelines-tab-running"
+ :class="{ 'active': scope === 'running'}">
+ <a :href="paths.runningPath">
+ Running
+ <span class="badge">
+ {{count.running}}
+ </span>
+ </a>
+ </li>
+ <li class="js-pipelines-tab-finished"
+ :class="{ 'active': scope === 'finished'}">
+ <a :href="paths.finishedPath">
+ Finished
+ <span class="badge">
+ {{count.finished}}
+ </span>
+ </a>
+ </li>
+ <li class="js-pipelines-tab-branches"
+ :class="{ 'active': scope === 'branches'}">
+ <a :href="paths.branchesPath">Branches</a>
+ </li>
+ <li class="js-pipelines-tab-tags"
+ :class="{ 'active': scope === 'tags'}">
+ <a :href="paths.tagsPath">Tags</a>
+ </li>
+ </ul>
+ `,
+};
diff --git a/app/assets/javascripts/vue_pipelines_index/index.js b/app/assets/javascripts/vue_pipelines_index/index.js
index b4e2d3a1143..48f9181a8d9 100644
--- a/app/assets/javascripts/vue_pipelines_index/index.js
+++ b/app/assets/javascripts/vue_pipelines_index/index.js
@@ -1,28 +1,22 @@
+import Vue from 'vue';
import PipelinesStore from './stores/pipelines_store';
import PipelinesComponent from './pipelines';
import '../vue_shared/vue_resource_interceptor';
-const Vue = window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
-
$(() => new Vue({
- el: document.querySelector('.vue-pipelines-index'),
+ el: document.querySelector('#pipelines-list-vue'),
data() {
- const project = document.querySelector('.pipelines');
const store = new PipelinesStore();
return {
store,
- endpoint: project.dataset.url,
};
},
components: {
'vue-pipelines': PipelinesComponent,
},
template: `
- <vue-pipelines
- :endpoint="endpoint"
- :store="store" />
+ <vue-pipelines :store="store" />
`,
}));
diff --git a/app/assets/javascripts/vue_pipelines_index/pipelines.js b/app/assets/javascripts/vue_pipelines_index/pipelines.js
index f389e5e4950..48f0e9036e8 100644
--- a/app/assets/javascripts/vue_pipelines_index/pipelines.js
+++ b/app/assets/javascripts/vue_pipelines_index/pipelines.js
@@ -1,19 +1,15 @@
-/* global Flash */
-/* eslint-disable no-new */
-import '~/flash';
import Vue from 'vue';
import PipelinesService from './services/pipelines_service';
import eventHub from './event_hub';
import PipelinesTableComponent from '../vue_shared/components/pipelines_table';
import TablePaginationComponent from '../vue_shared/components/table_pagination';
+import EmptyState from './components/empty_state';
+import ErrorState from './components/error_state';
+import NavigationTabs from './components/navigation_tabs';
+import NavigationControls from './components/nav_controls';
export default {
props: {
- endpoint: {
- type: String,
- required: true,
- },
-
store: {
type: Object,
required: true,
@@ -23,17 +19,109 @@ export default {
components: {
'gl-pagination': TablePaginationComponent,
'pipelines-table-component': PipelinesTableComponent,
+ 'empty-state': EmptyState,
+ 'error-state': ErrorState,
+ 'navigation-tabs': NavigationTabs,
+ 'navigation-controls': NavigationControls,
},
data() {
+ const pipelinesData = document.querySelector('#pipelines-list-vue').dataset;
+
return {
+ endpoint: pipelinesData.endpoint,
+ cssClass: pipelinesData.cssClass,
+ helpPagePath: pipelinesData.helpPagePath,
+ newPipelinePath: pipelinesData.newPipelinePath,
+ canCreatePipeline: pipelinesData.canCreatePipeline,
+ allPath: pipelinesData.allPath,
+ pendingPath: pipelinesData.pendingPath,
+ runningPath: pipelinesData.runningPath,
+ finishedPath: pipelinesData.finishedPath,
+ branchesPath: pipelinesData.branchesPath,
+ tagsPath: pipelinesData.tagsPath,
+ hasCi: pipelinesData.hasCi,
+ ciLintPath: pipelinesData.ciLintPath,
state: this.store.state,
apiScope: 'all',
pagenum: 1,
- pageRequest: false,
+ isLoading: false,
+ hasError: false,
};
},
+ computed: {
+ canCreatePipelineParsed() {
+ return gl.utils.convertPermissionToBoolean(this.canCreatePipeline);
+ },
+
+ scope() {
+ const scope = gl.utils.getParameterByName('scope');
+ return scope === null ? 'all' : scope;
+ },
+
+ shouldRenderErrorState() {
+ return this.hasError && !this.isLoading;
+ },
+
+ /**
+ * The empty state should only be rendered when the request is made to fetch all pipelines
+ * and none is returned.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderEmptyState() {
+ return !this.isLoading &&
+ !this.hasError &&
+ !this.state.pipelines.length &&
+ (this.scope === 'all' || this.scope === null);
+ },
+
+ /**
+ * When a specific scope does not have pipelines we render a message.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderNoPipelinesMessage() {
+ return !this.isLoading &&
+ !this.hasError &&
+ !this.state.pipelines.length &&
+ this.scope !== 'all' &&
+ this.scope !== null;
+ },
+
+ shouldRenderTable() {
+ return !this.hasError &&
+ !this.isLoading && this.state.pipelines.length;
+ },
+
+ /**
+ * Pagination should only be rendered when there is more than one page.
+ *
+ * @return {Boolean}
+ */
+ shouldRenderPagination() {
+ return !this.isLoading &&
+ this.state.pipelines.length &&
+ this.state.pageInfo.total > this.state.pageInfo.perPage;
+ },
+
+ hasCiEnabled() {
+ return this.hasCi !== undefined;
+ },
+
+ paths() {
+ return {
+ allPath: this.allPath,
+ pendingPath: this.pendingPath,
+ finishedPath: this.finishedPath,
+ runningPath: this.runningPath,
+ branchesPath: this.branchesPath,
+ tagsPath: this.tagsPath,
+ };
+ },
+ },
+
created() {
this.service = new PipelinesService(this.endpoint);
@@ -69,7 +157,7 @@ export default {
const pageNumber = gl.utils.getParameterByName('page') || this.pagenum;
const scope = gl.utils.getParameterByName('scope') || this.apiScope;
- this.pageRequest = true;
+ this.isLoading = true;
return this.service.getPipelines(scope, pageNumber)
.then(resp => ({
headers: resp.headers,
@@ -81,41 +169,72 @@ export default {
this.store.storePagination(response.headers);
})
.then(() => {
- this.pageRequest = false;
+ this.isLoading = false;
})
.catch(() => {
- this.pageRequest = false;
- new Flash('An error occurred while fetching the pipelines, please reload the page again.');
+ this.hasError = true;
+ this.isLoading = false;
});
},
},
- template: `
- <div>
- <div class="pipelines realtime-loading" v-if="pageRequest">
- <i class="fa fa-spinner fa-spin" aria-hidden="true"></i>
- </div>
- <div class="blank-state blank-state-no-icon"
- v-if="!pageRequest && state.pipelines.length === 0">
- <h2 class="blank-state-title js-blank-state-title">
- No pipelines to show
- </h2>
+ template: `
+ <div :class="cssClass">
+
+ <div
+ class="top-area"
+ v-if="!isLoading && !shouldRenderEmptyState">
+ <navigation-tabs
+ :scope="scope"
+ :count="state.count"
+ :paths="paths" />
+
+ <navigation-controls
+ :new-pipeline-path="newPipelinePath"
+ :has-ci-enabled="hasCiEnabled"
+ :help-page-path="helpPagePath"
+ :ciLintPath="ciLintPath"
+ :can-create-pipeline="canCreatePipelineParsed " />
</div>
- <div class="table-holder" v-if="!pageRequest && state.pipelines.length">
- <pipelines-table-component
- :pipelines="state.pipelines"
- :service="service"/>
+ <div class="content-list pipelines">
+
+ <div
+ class="realtime-loading"
+ v-if="isLoading">
+ <i
+ class="fa fa-spinner fa-spin"
+ aria-hidden="true" />
+ </div>
+
+ <empty-state
+ v-if="shouldRenderEmptyState"
+ :help-page-path="helpPagePath" />
+
+ <error-state v-if="shouldRenderErrorState" />
+
+ <div
+ class="blank-state blank-state-no-icon"
+ v-if="shouldRenderNoPipelinesMessage">
+ <h2 class="blank-state-title js-blank-state-title">No pipelines to show.</h2>
+ </div>
+
+ <div
+ class="table-holder"
+ v-if="shouldRenderTable">
+
+ <pipelines-table-component
+ :pipelines="state.pipelines"
+ :service="service"/>
+ </div>
+
+ <gl-pagination
+ v-if="shouldRenderPagination"
+ :pagenum="pagenum"
+ :change="change"
+ :count="state.count.all"
+ :pageInfo="state.pageInfo"/>
</div>
-
- <gl-pagination
- v-if="!pageRequest && state.pipelines.length && state.pageInfo.total > state.pageInfo.perPage"
- :pagenum="pagenum"
- :change="change"
- :count="state.count.all"
- :pageInfo="state.pageInfo"
- >
- </gl-pagination>
</div>
`,
};
diff --git a/app/assets/javascripts/vue_shared/common_vue.js b/app/assets/javascripts/vue_shared/common_vue.js
new file mode 100644
index 00000000000..eb2a6071fda
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/common_vue.js
@@ -0,0 +1,6 @@
+import Vue from 'vue';
+import './vue_resource_interceptor';
+
+if (process.env.NODE_ENV !== 'production') {
+ Vue.config.productionTip = false;
+}
diff --git a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
index f1c1e553b16..d5f87588c28 100644
--- a/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
+++ b/app/assets/javascripts/vue_shared/vue_resource_interceptor.js
@@ -1,20 +1,23 @@
-/* eslint-disable no-param-reassign, no-plusplus */
import Vue from 'vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);
+// Maintain a global counter for active requests
+// see: spec/support/wait_for_vue_resource.rb
Vue.http.interceptors.push((request, next) => {
- Vue.activeResources = Vue.activeResources ? Vue.activeResources + 1 : 1;
+ window.activeVueResources = window.activeVueResources || 0;
+ window.activeVueResources += 1;
next(() => {
- Vue.activeResources--;
+ window.activeVueResources -= 1;
});
});
+// Inject CSRF token so we don't break any tests.
Vue.http.interceptors.push((request, next) => {
- // needed in order to not break the tests.
if ($.rails) {
+ // eslint-disable-next-line no-param-reassign
request.headers['X-CSRF-Token'] = $.rails.csrfToken();
}
next();
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index 546718ddaf8..1ae144fb471 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -92,6 +92,10 @@
.award-menu-holder {
display: inline-block;
position: relative;
+
+ .tooltip {
+ white-space: nowrap;
+ }
}
.award-control {
@@ -124,6 +128,10 @@
&:focus {
outline: 0;
}
+
+ .award-control-icon {
+ margin: 0;
+ }
}
&.is-loading {
@@ -153,6 +161,7 @@
.award-control-icon {
color: $border-gray-normal;
margin-top: 1px;
+ padding: 0 2px;
}
.award-control-text {
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index cda46223492..50849e95541 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -68,23 +68,19 @@
}
@mixin btn-green {
- @include btn-color($green-light, $border-green-light, $green-normal, $border-green-normal, $green-dark, $border-green-dark, $white-light);
+ @include btn-color($green-500, $green-600, $green-600, $green-700, $green-700, $green-800, $white-light);
}
@mixin btn-blue {
- @include btn-color($blue-light, $border-blue-light, $blue-normal, $border-blue-normal, $blue-dark, $border-blue-dark, $white-light);
-}
-
-@mixin btn-blue-medium {
- @include btn-color($blue-medium-light, $border-blue-light, $blue-medium, $border-blue-normal, $blue-medium-dark, $border-blue-dark, $white-light);
+ @include btn-color($blue-500, $blue-600, $blue-600, $blue-700, $blue-700, $blue-800, $white-light);
}
@mixin btn-orange {
- @include btn-color($orange-light, $border-orange-light, $orange-normal, $border-orange-normal, $orange-dark, $border-orange-dark, $white-light);
+ @include btn-color($orange-500, $orange-600, $orange-600, $orange-700, $orange-700, $orange-800, $white-light);
}
@mixin btn-red {
- @include btn-color($red-light, $border-red-light, $red-normal, $border-red-normal, $red-dark, $border-red-dark, $white-light);
+ @include btn-color($red-500, $red-600, $red-600, $red-700, $red-700, $red-800, $white-light);
}
@mixin btn-gray {
@@ -145,11 +141,11 @@
&.btn-new,
&.btn-create,
&.btn-save {
- @include btn-outline($white-light, $border-green-light, $border-green-light, $green-light, $white-light, $border-green-light, $green-normal, $border-green-normal);
+ @include btn-outline($white-light, $green-600, $green-500, $green-500, $white-light, $green-600, $green-600, $green-700);
}
&.btn-remove {
- @include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
+ @include btn-outline($white-light, $red-500, $red-500, $red-500, $white-light, $red-600, $red-600, $red-700);
}
}
@@ -157,11 +153,8 @@
@include btn-gray;
}
- &.btn-primary {
- @include btn-blue-medium;
- }
-
&.btn-info,
+ &.btn-primary,
&.btn-register {
@include btn-blue;
}
@@ -171,11 +164,11 @@
}
&.btn-close {
- @include btn-outline($white-light, $border-orange-light, $border-orange-light, $orange-light, $white-light, $border-orange-light, $orange-normal, $border-orange-normal);
+ @include btn-outline($white-light, $orange-600, $orange-500, $orange-500, $white-light, $orange-600, $orange-600, $orange-700);
}
&.btn-spam {
- @include btn-outline($white-light, $border-red-light, $border-red-light, $red-light, $white-light, $border-red-light, $red-normal, $border-red-normal);
+ @include btn-outline($white-light, $red-500, $red-500, $red-500, $white-light, $red-600, $red-600, $red-700);
}
&.btn-danger,
@@ -360,7 +353,7 @@
.btn-inverted {
&-secondary {
- @include btn-outline($white-light, $border-blue-light, $border-blue-light, $blue-light, $white-light, $border-blue-light, $blue-normal, $border-blue-normal);
+ @include btn-outline($white-light, $blue-500, $blue-500, $blue-500, $white-light, $blue-600, $blue-600, $blue-700);
}
}
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index 186bb9ac616..da5b754aec7 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -195,7 +195,7 @@
text-decoration: none;
.badge {
- background-color: darken($row-hover, 5%);
+ background-color: darken($dropdown-link-hover-bg, 5%);
}
}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 25d6fbe465a..432024779fd 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -177,37 +177,45 @@ label {
}
.gl-field-error {
- color: $red-normal;
+ color: $red-500;
}
.gl-show-field-errors {
.gl-field-success-outline {
- border: 1px solid $green-normal;
+ border: 1px solid $green-600;
&:focus {
- box-shadow: 0 0 0 1px $green-normal inset, 0 1px 1px $gl-field-focus-shadow inset, 0 0 4px 0 $green-normal;
+ box-shadow: 0 0 0 1px $green-600 inset, 0 1px 1px $gl-field-focus-shadow inset, 0 0 4px 0 $green-600;
border: 0 none;
}
}
.gl-field-error-outline {
- border: 1px solid $red-normal;
+ border: 1px solid $red-500;
&:focus {
- box-shadow: 0 0 0 1px $red-normal inset, 0 1px 1px $gl-field-focus-shadow inset, 0 0 4px 0 $gl-field-focus-shadow-error;
+ box-shadow: 0 0 0 1px $red-500 inset, 0 1px 1px $gl-field-focus-shadow inset, 0 0 4px 0 $gl-field-focus-shadow-error;
border: 0 none;
}
}
.gl-field-success-message {
- color: $green-normal;
+ color: $green-600;
}
.gl-field-error-message {
- color: $red-normal;
+ color: $red-500;
}
.gl-field-hint {
color: $gl-text-color;
}
}
+
+@media(max-width: $screen-xs-max) {
+ .remember-me {
+ .remember-me-checkbox {
+ margin-top: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 6660a022260..fa02598760f 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -265,7 +265,7 @@ header {
}
.impersonation i {
- color: $red-normal;
+ color: $red-500;
}
}
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index db8d231a82a..87667f39ab8 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -1,8 +1,8 @@
.ci-status-icon-success {
- color: $gl-success;
+ color: $green-500;
svg {
- fill: $gl-success;
+ fill: $green-500;
}
}
@@ -17,18 +17,18 @@
.ci-status-icon-pending,
.ci-status-icon-failed_with_warnings,
.ci-status-icon-success_with_warnings {
- color: $gl-warning;
+ color: $orange-500;
svg {
- fill: $gl-warning;
+ fill: $orange-500;
}
}
.ci-status-icon-running {
- color: $blue-normal;
+ color: $blue-400;
svg {
- fill: $blue-normal;
+ fill: $blue-400;
}
}
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 46632f15f35..1537b0744cc 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -33,7 +33,7 @@
}
&.status-box-open {
- background-color: $green-light;
+ background-color: $green-500;
}
&.status-box-expired {
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index 0a42b17c1f5..20c7bc93c28 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -23,6 +23,10 @@ body {
}
}
+.content-wrapper {
+ padding-bottom: 100px;
+}
+
.container {
padding-top: 0;
z-index: 5;
@@ -72,28 +76,28 @@ body {
/* Stripe the background colors so that adjacent alert-warnings are distinct from one another */
.alert-warning {
transition: background-color 0.15s, border-color 0.15s;
- background-color: lighten($gl-warning, 4%);
- border-color: lighten($gl-warning, 4%);
+ background-color: $orange-500;
+ border-color: $orange-500;
}
.alert-warning + .alert-warning {
- background-color: $gl-warning;
- border-color: $gl-warning;
+ background-color: $orange-600;
+ border-color: $orange-600;
}
.alert-warning + .alert-warning + .alert-warning {
- background-color: darken($gl-warning, 4%);
- border-color: darken($gl-warning, 4%);
+ background-color: $orange-700;
+ border-color: $orange-700;
}
.alert-warning + .alert-warning + .alert-warning + .alert-warning {
- background-color: darken($gl-warning, 8%);
- border-color: darken($gl-warning, 8%);
+ background-color: $orange-800;
+ border-color: $orange-800;
}
.alert-warning:only-of-type {
- background-color: $gl-warning;
- border-color: $gl-warning;
+ background-color: $orange-500;
+ border-color: $orange-500;
}
}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 7adbb0a4188..15dc0aa6a52 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -122,7 +122,7 @@ ul.content-list {
}
.member-group-link {
- color: $blue-normal;
+ color: $blue-600;
}
.description {
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 205d23b1329..5ab505034b6 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -416,14 +416,16 @@
.page-with-layout-nav {
.right-sidebar {
- top: ($header-height * 2) + 2;
+ top: ($header-height + 1) * 2;
}
- .build-sidebar {
- top: ($header-height * 3) + 3;
+ &.page-with-sub-nav {
+ .right-sidebar {
+ top: ($header-height + 1) * 3;
- &.affix {
- top: 0;
+ &.affix {
+ top: 0;
+ }
}
}
}
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index 12a86a64645..e54cc2866a7 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -176,6 +176,10 @@ summary {
&.panel-without-border {
border: 0;
}
+
+ &.panel-without-margin {
+ margin: 0;
+ }
}
.panel-succes .panel-heading,
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index 0fc89d5976a..c9f345d24be 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -31,6 +31,7 @@ $border-radius-small: 3px !default;
//
$text-color: $gl-text-color;
$link-color: $gl-link-color;
+$link-hover-color: $gl-link-hover-color;
//== Typography
@@ -73,7 +74,7 @@ $pagination-hover-color: $gl-text-color;
$pagination-hover-bg: $row-hover;
$pagination-hover-border: $border-color;
-$pagination-active-color: $blue-dark;
+$pagination-active-color: $blue-600;
$pagination-active-bg: $white-light;
$pagination-active-border: $border-color;
@@ -135,8 +136,8 @@ $well-border: #eee;
//
//##
-$code-color: #c7254e;
-$code-bg: #f9f2f4;
+$code-color: $red-600;
+$code-bg: lighten($red-50, 2%);
$kbd-color: $white-light;
$kbd-bg: #333;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 6841adb637e..97794a47df8 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -26,27 +26,49 @@ $gray-dark: darken($gray-light, $darken-dark-factor);
$gray-darker: #eee;
$gray-darkest: #c4c4c4;
-$green-light: #3cbd70;
-$green-normal: darken($green-light, $darken-normal-factor);
-$green-dark: darken($green-light, $darken-dark-factor);
-
-$blue-light: #2ea8e5;
-$blue-normal: darken($blue-light, $darken-normal-factor);
-$blue-dark: darken($blue-light, $darken-dark-factor);
-
-$blue-medium-light: #3498cb;
-$blue-medium: darken($blue-medium-light, $darken-normal-factor);
-$blue-medium-dark: darken($blue-medium-light, $darken-dark-factor);
-
-$blue-light-transparent: rgba(44, 159, 216, 0.05);
-
-$orange-light: #fc8a51;
-$orange-normal: darken($orange-light, $darken-normal-factor);
-$orange-dark: darken($orange-light, $darken-dark-factor);
-
-$red-light: #e52c5a;
-$red-normal: darken($red-light, $darken-normal-factor);
-$red-dark: darken($red-light, $darken-dark-factor);
+$green-50: #e4f5eb;
+$green-100: #bae6cc;
+$green-200: #8dd5aa;
+$green-300: #5fc488;
+$green-400: #3cb76f;
+$green-500: #1aaa55;
+$green-600: #168f48;
+$green-700: #12753a;
+$green-800: #0e5a2d;
+$green-900: #0a4020;
+
+$blue-50: #e4eff9;
+$blue-100: #bcd7f1;
+$blue-200: #8fbce8;
+$blue-300: #62a1df;
+$blue-400: #418cd8;
+$blue-500: #1f78d1;
+$blue-600: #1b69b6;
+$blue-700: #17599c;
+$blue-800: #134a81;
+$blue-900: #0f3b66;
+
+$orange-50: #fff2e1;
+$orange-100: #fedfb3;
+$orange-200: #feca81;
+$orange-300: #fdb44f;
+$orange-400: #fca429;
+$orange-500: #fc9403;
+$orange-600: #de7e00;
+$orange-700: #c26700;
+$orange-800: #a35100;
+$orange-900: #853b00;
+
+$red-50: #fbe7e4;
+$red-100: #f4c4bc;
+$red-200: #ed9d90;
+$red-300: #e67664;
+$red-400: #e05842;
+$red-500: #db3b21;
+$red-600: #c0341d;
+$red-700: #a62d19;
+$red-800: #8b2615;
+$red-900: #711e11;
$black: #000;
$black-transparent: rgba(0, 0, 0, 0.3);
@@ -58,33 +80,11 @@ $border-gray-light: darken($gray-light, $darken-border-factor);
$border-gray-normal: darken($gray-normal, $darken-border-factor);
$border-gray-dark: darken($white-normal, $darken-border-factor);
-$border-green-extra-light: #9adb84;
-$border-green-light: darken($green-light, $darken-border-factor);
-$border-green-normal: darken($green-normal, $darken-border-factor);
-$border-green-dark: darken($green-dark, $darken-border-factor);
-
-$border-blue-light: darken($blue-light, $darken-border-factor);
-$border-blue-normal: darken($blue-normal, $darken-border-factor);
-$border-blue-dark: darken($blue-dark, $darken-border-factor);
-
-$border-orange-light: darken($orange-light, $darken-border-factor);
-$border-orange-normal: darken($orange-normal, $darken-border-factor);
-$border-orange-dark: darken($orange-dark, $darken-border-factor);
-
-$border-red-light: darken($red-light, $darken-border-factor);
-$border-red-normal: darken($red-normal, $darken-border-factor);
-$border-red-dark: darken($red-dark, $darken-border-factor);
-
-$warning-message-bg: #fbf2d9;
-$warning-message-color: #9e8e60;
-$warning-message-border: #f0e2bb;
-
/*
* UI elements
*/
$border-color: #e5e5e5;
-$focus-border-color: #3aabf0;
-$sidebar-collapsed-icon-color: #999;
+$focus-border-color: $blue-300;
$well-expand-item: #e8f2f7;
$well-inner-border: #eef0f2;
$well-light-border: #f1f1f1;
@@ -97,10 +97,11 @@ $gl-font-size: 14px;
$gl-text-color: rgba(0, 0, 0, .85);
$gl-text-color-secondary: rgba(0, 0, 0, .55);
$gl-text-color-disabled: rgba(0, 0, 0, .35);
-$gl-text-green: #4a2;
-$gl-text-red: #d12f19;
-$gl-text-orange: #d90;
-$gl-link-color: #3777b0;
+$gl-text-green: $green-600;
+$gl-text-red: $red-500;
+$gl-text-orange: $orange-600;
+$gl-link-color: $blue-600;
+$gl-link-hover-color: $blue-800;
$gl-grayish-blue: #7f8fa4;
$gl-gray: $gl-text-color;
$gl-gray-dark: #313236;
@@ -117,9 +118,9 @@ $list-text-disabled-color: $gl-text-color-disabled;
$list-border-light: #eee;
$list-border: rgba(0, 0, 0, 0.05);
$list-text-height: 42px;
-$list-warning-row-bg: #fcf8e3;
-$list-warning-row-border: #faebcc;
-$list-warning-row-color: #8a6d3b;
+$list-warning-row-bg: $orange-50;
+$list-warning-row-border: $orange-100;
+$list-warning-row-color: $orange-700;
/*
* Markdown
@@ -146,24 +147,24 @@ $gl-sidebar-padding: 22px;
/*
* Misc
*/
-$row-hover: #f7faff;
-$row-hover-border: #b2d7ff;
+$row-hover: lighten($blue-50, 2%);
+$row-hover-border: $blue-100;
$progress-color: #c0392b;
$header-height: 50px;
$fixed-layout-width: 1280px;
$limited-layout-width: 990px;
$gl-avatar-size: 40px;
-$error-exclamation-point: #e62958;
+$error-exclamation-point: $red-500;
$border-radius-default: 2px;
$settings-icon-size: 18px;
-$provider-btn-not-active-color: #4688f1;
-$link-underline-blue: #4a8bee;
-$active-item-blue: #4a8bee;
+$provider-btn-not-active-color: $blue-500;
+$link-underline-blue: $blue-500;
+$active-item-blue: $blue-500;
$layout-link-gray: #7e7c7c;
$btn-side-margin: 10px;
$btn-sm-side-margin: 7px;
$btn-xs-side-margin: 5px;
-$issue-status-expired: #cea61b;
+$issue-status-expired: $orange-500;
$issuable-sidebar-color: $gl-text-color-secondary;
$show-aside-bg: #eee;
$show-aside-color: #777;
@@ -192,10 +193,10 @@ $user-mention-color: #2fa0bb;
$time-color: #999;
$project-member-show-color: #aaa;
$gl-promo-color: #aaa;
-$error-bg: #c67;
-$warning-message-bg: #ffffe6;
-$warning-message-border: #ed9;
-$warning-message-color: #b90;
+$error-bg: $red-400;
+$warning-message-bg: $orange-50;
+$warning-message-border: $orange-100;
+$warning-message-color: $orange-700;
$control-group-descr-color: #666;
$table-permission-x-bg: #d9edf7;
$username-color: #666;
@@ -210,30 +211,30 @@ $tanuki-yellow: #fca326;
/*
* State colors:
*/
-$gl-primary: $blue-normal;
-$gl-success: $green-normal;
+$gl-primary: $blue-500;
+$gl-success: $green-500;
$gl-success-focus: rgba($gl-success, .4);
-$gl-info: $blue-normal;
-$gl-warning: $orange-normal;
-$gl-danger: $red-normal;
+$gl-info: $blue-500;
+$gl-warning: $orange-500;
+$gl-danger: $red-500;
$gl-btn-active-background: rgba(0, 0, 0, 0.16);
$gl-btn-active-gradient: inset 0 2px 3px $gl-btn-active-background;
/*
* Commit Diff Colors
*/
-$added: #63c363;
-$deleted: #f77;
-$line-added: #ecfdf0;
-$line-added-dark: #c7f0d2;
-$line-removed: #fbe9eb;
-$line-removed-dark: #fac5cd;
-$line-number-old: #f9d7dc;
-$line-number-new: #ddfbe6;
-$line-number-select: #fbf2da;
-$line-target-blue: #f6faff;
-$line-select-yellow: #fcf8e7;
-$line-select-yellow-dark: #f0e2bd;
+$added: $green-300;
+$deleted: $red-300;
+$line-added: $green-50;
+$line-added-dark: $green-100;
+$line-removed: $red-50;
+$line-removed-dark: $red-100;
+$line-number-old: lighten($red-100, 5%);
+$line-number-new: lighten($green-100, 5%);
+$line-number-select: lighten($orange-100, 5%);
+$line-target-blue: $blue-50;
+$line-select-yellow: $orange-50;
+$line-select-yellow-dark: $orange-100;
$dark-diff-match-bg: rgba(255, 255, 255, 0.3);
$dark-diff-match-color: rgba(255, 255, 255, 0.1);
$file-mode-changed: #777;
@@ -273,7 +274,7 @@ $dropdown-toggle-active-border-color: darken($border-color, 14%);
/*
* Filtered Search
*/
-$dropdown-hover-color: #3b86ff;
+$dropdown-hover-color: $blue-400;
/*
* Buttons
@@ -296,10 +297,10 @@ $award-emoji-menu-shadow: rgba(0,0,0,.175);
/*
* Search Box
*/
-$search-input-border-color: rgba(#4688f1, .8);
+$search-input-border-color: rgba($blue-400, .8);
$search-input-focus-shadow-color: $dropdown-input-focus-shadow;
$search-input-width: 220px;
-$location-badge-active-bg: #4f91f8;
+$location-badge-active-bg: $blue-500;
$location-icon-color: #e7e9ed;
/*
@@ -361,18 +362,18 @@ $builds-trace-bg: #111;
/*
* Callout
*/
-$callout-danger-bg: #fdf7f7;
-$callout-danger-border: #eed3d7;
-$callout-danger-color: #b94a48;
-$callout-warning-bg: #faf8f0;
-$callout-warning-border: #faebcc;
-$callout-warning-color: #8a6d3b;
-$callout-info-bg: #f4f8fa;
-$callout-info-border: #bce8f1;
-$callout-info-color: #34789a;
-$callout-success-bg: #dff0d8;
-$callout-success-border: #5ca64d;
-$callout-success-color: #3c763d;
+$callout-danger-bg: $red-50;
+$callout-danger-border: $red-100;
+$callout-danger-color: $red-700;
+$callout-warning-bg: $orange-50;
+$callout-warning-border: $orange-100;
+$callout-warning-color: $orange-700;
+$callout-info-bg: $blue-50;
+$callout-info-border: $blue-100;
+$callout-info-color: $blue-700;
+$callout-success-bg: $green-50;
+$callout-success-border: $green-100;
+$callout-success-color: $green-700;
/*
* Commit Page
@@ -392,7 +393,7 @@ $common-green: $gl-text-green;
/*
* Editor
*/
-$editor-cancel-color: #b94a48;
+$editor-cancel-color: $red-600;
/*
* Events
@@ -416,10 +417,10 @@ $logs-p-color: #333;
* Forms
*/
$input-danger-bg: #f2dede;
-$input-danger-border: #d66;
+$input-danger-border: $red-400;
$input-group-addon-bg: #f7f8fa;
$gl-field-focus-shadow: rgba(0, 0, 0, 0.075);
-$gl-field-focus-shadow-error: rgba(210, 40, 82, 0.6);
+$gl-field-focus-shadow-error: rgba($red-500, 0.6);
/*
* Help
@@ -453,14 +454,14 @@ $label-border-radius: 100px;
/*
* Lint
*/
-$lint-incorrect-color: red;
-$lint-correct-color: #47a447;
+$lint-incorrect-color: $red-500;
+$lint-correct-color: $green-500;
/*
* Login
*/
$login-brand-holder-color: #888;
-$login-devise-error-color: #a00;
+$login-devise-error-color: $red-700;
/*
* Nav
@@ -474,33 +475,33 @@ $nav-toggle-gray: #666;
*/
$notify-details: #777;
$notify-footer: #777;
-$notify-new-file: #090;
-$notify-deleted-file: #b00;
+$notify-new-file: $green-600;
+$notify-deleted-file: $red-700;
/*
* Projects
*/
$project-option-descr-color: #54565b;
$project-breadcrumb-color: #999;
-$project-private-forks-notice-odd: #2aa056;
+$project-private-forks-notice-odd: $green-600;
$project-network-controls-color: #888;
/*
* Runners
*/
-$runner-state-shared-bg: #32b186;
-$runner-state-specific-bg: #3498db;
-$runner-status-online-color: $green-normal;
+$runner-state-shared-bg: $green-400;
+$runner-state-specific-bg: $blue-400;
+$runner-status-online-color: $green-600;
$runner-status-offline-color: $gray-darkest;
-$runner-status-paused-color: $red-normal;
+$runner-status-paused-color: $red-500;
/*
Stat Graph
*/
$stat-graph-common-bg: #f3f3f3;
-$stat-graph-area-fill: #1db34f;
+$stat-graph-area-fill: $green-500;
$stat-graph-axis-fill: #aaa;
-$stat-graph-orange-fill: #f17f49;
+$stat-graph-orange-fill: $orange-500;
$stat-graph-selection-fill: #333;
$stat-graph-selection-stroke: #333;
@@ -514,7 +515,7 @@ $select2-drop-shadow2: rgba(31, 37, 50, 0.317647);
/*
* Todo
*/
-$todo-alert-blue: #428bca;
+$todo-alert-blue: $blue-500;
$todo-body-pre-color: #777;
$todo-body-border: #ddd;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index f9ee33019cd..575d32b1a23 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -487,9 +487,9 @@
right: -3px;
top: -3px;
width: 17px;
- background-color: $blue-light;
+ background-color: $blue-500;
color: $white-light;
- border: 1px solid $border-blue-light;
+ border: 1px solid $blue-600;
font-size: 9px;
line-height: 15px;
border-radius: 50%;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index a24292a7c8c..969fc75c6eb 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -366,9 +366,3 @@
right: 0;
margin-top: -17px;
}
-
-@media (min-width: $screen-md-min) {
- .sub-nav.build {
- width: calc(100% + #{$gutter_width});
- }
-}
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 73a5da715f2..25be7f408d0 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -18,7 +18,10 @@
.environments-container {
.table-holder {
width: 100%;
- overflow: auto;
+
+ @media (max-width: $screen-sm-max) {
+ overflow: auto;
+ }
}
.table.ci-table {
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 4426169ef5a..c1a9bc4be28 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -498,7 +498,7 @@
svg {
width: 16px;
height: 16px;
- fill: $sidebar-collapsed-icon-color;
+ fill: $issuable-sidebar-color;
}
&:hover svg {
@@ -520,12 +520,12 @@
&.over_estimate {
.meter-fill {
- background: $red-light;
+ background: $red-500;
}
.time-remaining,
.compare-value.spent {
- color: $red-light;
+ color: $red-500;
}
}
}
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index cb7ebd61504..b2f45625a2a 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -46,6 +46,10 @@ ul.related-merge-requests > li {
.merge-request-id {
flex-shrink: 0;
}
+
+ .merge-request-info {
+ margin-left: 5px;
+ }
}
.merge-requests-title,
@@ -58,10 +62,6 @@ ul.related-merge-requests > li {
display: inline-block;
}
-.merge-request-info {
- margin-left: 5px;
-}
-
.merge-request-status {
font-size: 13px;
padding: 0 5px;
@@ -69,21 +69,17 @@ ul.related-merge-requests > li {
height: 20px;
border-radius: 3px;
line-height: 18px;
- border: 1px solid;
&.merged {
- border-color: darken($blue-normal, 10%);
- background: $blue-normal;
+ background: $blue-500;
}
&.closed {
- border-color: darken($red-normal, 10%);
- background: $red-normal;
+ background: $red-500;
}
&.open {
- border: 1px solid darken($green-normal, 10%);
- background: $green-normal;
+ background: $green-500;
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 71ed5b1361a..8249e02b64a 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -85,11 +85,11 @@
}
.username .validation-success {
- color: $green-normal;
+ color: $green-600;
}
.username .validation-error {
- color: $red-normal;
+ color: $red-500;
}
}
}
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index 5a9f199fb34..35cefd449f1 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -255,7 +255,7 @@ $colors: (
&.saved {
.editor {
- border-top: solid 2px $border-green-extra-light;
+ border-top: solid 2px $green-200;
}
}
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 7c3172421c1..6630904ec92 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -535,7 +535,7 @@
}
.fa-info-circle {
- color: $orange-normal;
+ color: $orange-500;
padding-right: 5px;
}
}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 27c47d36818..efbd9365fd9 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -63,7 +63,7 @@
}
.remaining-days {
- color: $orange-light;
+ color: $orange-600;
}
.milestone-stats-and-buttons {
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index e238f0865f6..a2129722633 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -462,17 +462,18 @@ ul.notes {
background: $white-light;
padding: 1px 5px;
font-size: 12px;
- color: $gl-link-color;
+ color: $blue-500;
margin-left: -55px;
position: absolute;
z-index: 10;
width: 23px;
height: 23px;
- border: 1px solid $border-color;
+ border: 1px solid $blue-500;
transition: transform .1s ease-in-out;
&:hover {
- background: $gl-info;
+ background: $blue-500;
+ border-color: $blue-600;
color: $white-light;
transform: scale(1.15);
}
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 33b38ca6923..a4fe652b52f 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -2,6 +2,7 @@
.realtime-loading {
font-size: 40px;
text-align: center;
+ margin: 0 auto;
}
.stage {
@@ -13,9 +14,16 @@
white-space: nowrap;
}
+ .empty-state {
+ margin: 5% auto 0;
+ }
+
.table-holder {
width: 100%;
- overflow: auto;
+
+ @media (max-width: $screen-sm-max) {
+ overflow: auto;
+ }
}
.commit-title {
@@ -99,8 +107,6 @@
@media (max-width: $screen-md-max) {
.content-list {
- &.pipelines,
- &.environments-container,
&.builds-content-list {
width: 100%;
overflow: auto;
@@ -667,51 +673,71 @@
// Dropdown button animation in mini pipeline graph
&.ci-status-icon-success {
- border-color: $gl-success;
- color: $gl-success;
+ border-color: $green-500;
+ color: $green-500;
&:hover,
&:focus,
&:active {
- background-color: rgba($gl-success, 0.1);
- border-color: $gl-success;
+ background-color: $green-50;
+ border-color: $green-600;
+ color: $green-600;
+
+ svg {
+ fill: $green-600;
+ }
}
}
&.ci-status-icon-failed {
- border-color: $gl-danger;
- color: $gl-danger;
+ border-color: $red-500;
+ color: $red-500;
&:hover,
&:focus,
&:active {
- background-color: rgba($gl-danger, 0.1);
- border-color: $gl-danger;
+ background-color: $red-50;
+ border-color: $red-600;
+ color: $red-600;
+
+ svg {
+ fill: $red-600;
+ }
}
}
&.ci-status-icon-pending,
&.ci-status-icon-success_with_warnings {
- border-color: $gl-warning;
- color: $gl-warning;
+ border-color: $orange-500;
+ color: $orange-500;
&:hover,
&:focus,
&:active {
- background-color: rgba($gl-warning, 0.1);
- border-color: $gl-warning;
+ background-color: $orange-50;
+ border-color: $orange-600;
+ color: $orange-600;
+
+ svg {
+ fill: $orange-600;
+ }
}
}
&.ci-status-icon-running {
- border-color: $blue-normal;
- color: $blue-normal;
+ border-color: $blue-400;
+ color: $blue-400;
&:hover,
&:focus,
&:active {
- background-color: rgba($blue-normal, 0.1);
- border-color: $blue-normal;
+ background-color: $blue-50;
+ border-color: $blue-600;
+ color: $blue-600;
+
+ svg {
+ fill: $blue-600;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index 1a983d8c9ef..703c5fc8869 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -74,7 +74,6 @@
display: inline;
a {
- color: $blue-dark;
text-decoration: none;
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index efa47be9a73..949d52cffa2 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -582,54 +582,55 @@ pre.light-well {
/*
* Projects list rendered on dashboard and user page
*/
-
.projects-list {
@include basic-list;
+ display: flex;
+ flex-direction: column;
.project-row {
- border-color: $white-normal;
-
- .project-full-name {
- @include str-truncated;
+ display: flex;
+ align-items: center;
+ }
- @media (max-width: $screen-xs-max) {
- max-width: 50%;
- }
- }
+ h3 {
+ font-size: $gl-font-size;
+ }
- .controls {
- line-height: $list-text-height;
+ a {
+ color: $gl-text-color;
+ }
- .badge {
- @media (max-width: $screen-xs-max) {
- display: none;
- }
- }
+ .avatar-container,
+ .controls {
+ flex: 0 0 auto;
+ }
- a:hover {
- text-decoration: none;
- }
+ .avatar-container {
+ align-self: flex-start;
+ }
- > span {
- margin-left: 10px;
- }
+ .project-details {
+ min-width: 0;
- svg {
- position: relative;
- top: 2px;
- }
+ p,
+ .commit-row-message {
+ @include str-truncated(100%);
+ margin-bottom: 0;
}
+ }
- .description p {
- @media (max-width: $screen-xs-max) {
- max-width: 50%;
- }
- }
+ .controls {
+ margin-left: auto;
}
- .bottom {
- padding-top: $gl-padding;
- padding-bottom: 0;
+ .ci-status-link {
+ display: inline-block;
+ line-height: 17px;
+ vertical-align: middle;
+
+ &:hover {
+ text-decoration: none;
+ }
}
}
diff --git a/app/assets/stylesheets/pages/sherlock.scss b/app/assets/stylesheets/pages/sherlock.scss
index bed6470dbd3..23a9c2ada80 100644
--- a/app/assets/stylesheets/pages/sherlock.scss
+++ b/app/assets/stylesheets/pages/sherlock.scss
@@ -28,6 +28,6 @@ table .sherlock-code {
}
.sherlock-line-samples-table .slow {
- color: $red-light;
+ color: $red-500;
font-weight: bold;
}
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 6f31d4ed789..4a284247143 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -21,42 +21,41 @@
&.ci-failed,
&.ci-failed_with_warnings {
- color: $gl-danger;
- border-color: $gl-danger;
+ color: $red-500;
+ border-color: $red-500;
&:not(span):hover {
- background-color: rgba($gl-danger, .07);
+ background-color: $red-50;
+ color: $red-600;
+ border-color: $red-600;
+
+ svg {
+ fill: $red-600;
+ }
}
svg {
- fill: $gl-danger;
+ fill: $red-500;
}
}
&.ci-success,
&.ci-success_with_warnings {
- color: $gl-success;
- border-color: $gl-success;
+ color: $green-600;
+ border-color: $green-500;
&:not(span):hover {
- background-color: rgba($gl-success, .07);
- }
-
- svg {
- fill: $gl-success;
- }
- }
-
- &.ci-info {
- color: $gl-info;
- border-color: $gl-info;
+ background-color: $green-50;
+ color: $green-700;
+ border-color: $green-600;
- &:not(span):hover {
- background-color: rgba($gl-info, .07);
+ svg {
+ fill: $green-600;
+ }
}
svg {
- fill: $gl-info;
+ fill: $green-500;
}
}
@@ -75,28 +74,41 @@
}
&.ci-pending {
- color: $gl-warning;
- border-color: $gl-warning;
+ color: $orange-600;
+ border-color: $orange-500;
&:not(span):hover {
- background-color: rgba($gl-warning, .07);
+ background-color: $orange-50;
+ color: $orange-700;
+ border-color: $orange-600;
+
+ svg {
+ fill: $orange-600;
+ }
}
svg {
- fill: $gl-warning;
+ fill: $orange-500;
}
}
+ &.ci-info,
&.ci-running {
- color: $blue-normal;
- border-color: $blue-normal;
+ color: $blue-500;
+ border-color: $blue-500;
&:not(span):hover {
- background-color: rgba($blue-normal, .07);
+ background-color: $blue-50;
+ color: $blue-600;
+ border-color: $blue-600;
+
+ svg {
+ fill: $blue-600;
+ }
}
svg {
- fill: $blue-normal;
+ fill: $blue-500;
}
}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 5f0aede4f5e..b071d7f18cd 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -47,6 +47,7 @@
.todo-avatar,
.todo-actions {
+ @include transition(opacity);
-webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
@@ -67,21 +68,34 @@
flex: 0 1 100%;
min-width: 0;
}
-}
-.todos-list > .todo.todo-pending.done-reversible {
- background-color: $gray-light;
+ &.todo-pending.done-reversible {
+ background-color: $white-light;
- &:hover {
- border-color: $border-color;
- }
+ &:hover {
+ border-color: $white-dark;
+ background-color: $gray-light;
- .title {
- font-weight: normal;
+ .todo-avatar,
+ .todo-item {
+ opacity: .6;
+ }
+ }
+
+ .todo-avatar,
+ .todo-item {
+ opacity: .2;
+ }
+
+ .btn {
+ background-color: $gray-light;
+ }
}
}
.todo-item {
+ @include transition(opacity);
+
.todo-title {
display: flex;
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 24504685e48..563bcc65bd6 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -95,18 +95,14 @@ class Admin::UsersController < Admin::ApplicationController
def create
opts = {
- force_random_password: true,
- password_expires_at: nil
+ reset_password: true,
+ skip_confirmation: true
}
- @user = User.new(user_params.merge(opts))
- @user.created_by_id = current_user.id
- @user.generate_password
- @user.generate_reset_token
- @user.skip_confirmation!
+ @user = Users::CreateService.new(current_user, user_params.merge(opts)).execute
respond_to do |format|
- if @user.save
+ if @user.persisted?
format.html { redirect_to [:admin, @user], notice: 'User was successfully created.' }
format.json { render json: @user, status: :created, location: @user }
else
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index b7ce081a5cd..6a6e335d314 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -64,8 +64,11 @@ class ApplicationController < ActionController::Base
# This filter handles both private tokens and personal access tokens
def authenticate_user_from_private_token!
- token_string = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
- user = User.find_by_authentication_token(token_string) || User.find_by_personal_access_token(token_string)
+ token = params[:private_token].presence || request.headers['PRIVATE-TOKEN'].presence
+
+ return unless token.present?
+
+ user = User.find_by_authentication_token(token) || User.find_by_personal_access_token(token)
if user && can?(user, :log_in)
# Notice we are passing store false, so the user is not
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 096de8032ae..498690e8f11 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -51,7 +51,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
private
def find_todos
- @todos ||= TodosFinder.new(current_user, params.merge(include_associations: true)).execute
+ @todos ||= TodosFinder.new(current_user, params).execute
end
def todos_counts
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index 8e42cdf415f..5ad1e116e4e 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -44,15 +44,15 @@ class Import::BitbucketController < Import::BaseController
repo_owner = repo.owner
repo_owner = current_user.username if repo_owner == bitbucket_client.user.username
- @target_namespace = params[:new_namespace].presence || repo_owner
+ namespace_path = params[:new_namespace].presence || repo_owner
- namespace = find_or_create_namespace(@target_namespace, current_user)
+ @target_namespace = find_or_create_namespace(namespace_path, current_user)
- if current_user.can?(:create_projects, namespace)
+ if current_user.can?(:create_projects, @target_namespace)
# The token in a session can be expired, we need to get most recent one because
# Bitbucket::Connection class refreshes it.
session[:bitbucket_token] = bitbucket_client.connection.token
- @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, namespace, current_user, credentials).execute
+ @project = Gitlab::BitbucketImport::ProjectCreator.new(repo, @project_name, @target_namespace, current_user, credentials).execute
else
render 'unauthorized'
end
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index e2f81b09adc..f1a93ccb3ad 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -89,4 +89,9 @@ class Projects::ApplicationController < ApplicationController
def builds_enabled
return render_404 unless @project.feature_available?(:builds, current_user)
end
+
+ def update_ref
+ branch_exists = @repository.find_branch(@target_branch)
+ @ref = @target_branch if branch_exists
+ end
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 52fc67d162c..80a95c6158b 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -89,11 +89,6 @@ class Projects::BlobController < Projects::ApplicationController
private
- def update_ref
- branch_exists = @repository.find_branch(@target_branch)
- @ref = @target_branch if branch_exists
- end
-
def blob
@blob ||= Blob.decorate(@repository.blob_at(@commit.id, @path))
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 886934a3f67..f1e4246e7fb 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -1,7 +1,7 @@
class Projects::BuildsController < Projects::ApplicationController
before_action :build, except: [:index, :cancel_all]
before_action :authorize_read_build!, except: [:cancel, :cancel_all, :retry, :play]
- before_action :authorize_update_build!, except: [:index, :show, :status, :raw]
+ before_action :authorize_update_build!, except: [:index, :show, :status, :raw, :trace]
layout 'project'
def index
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 1502b734f37..d0c44e297e3 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -31,8 +31,10 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def disable
- @project.deploy_keys_projects.find_by(deploy_key_id: params[:id]).destroy
+ deploy_key_project = @project.deploy_keys_projects.find_by(deploy_key_id: params[:id])
+ return render_404 unless deploy_key_project
+ deploy_key_project.destroy!
redirect_to_repository_settings(@project)
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index cdb5b4173d3..d984e6d3918 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -148,7 +148,14 @@ class Projects::IssuesController < Projects::ApplicationController
end
format.json do
- render json: @issue.to_json(include: { milestone: {}, assignee: { only: [:name, :username], methods: [:avatar_url] }, labels: { methods: :text_color } }, methods: [:task_status, :task_status_short])
+ if @issue.valid?
+ render json: @issue.to_json(methods: [:task_status, :task_status_short],
+ include: { milestone: {},
+ assignee: { only: [:name, :username], methods: [:avatar_url] },
+ labels: { methods: :text_color } })
+ else
+ render json: { errors: @issue.errors.full_messages }, status: :unprocessable_entity
+ end
end
end
@@ -253,4 +260,13 @@ class Projects::IssuesController < Projects::ApplicationController
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
)
end
+
+ def authenticate_user!
+ return if current_user
+
+ notice = "Please sign in to create the new issue."
+
+ store_location_for :user, request.fullpath
+ redirect_to new_user_session_path, notice: notice
+ end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 677a8a1a73a..2fadf7c8c81 100644..100755
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -402,7 +402,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if params[:ref].present?
@ref = params[:ref]
- @commit = @repository.commit(@ref)
+ @commit = @repository.commit("refs/heads/#{@ref}")
end
render layout: false
@@ -413,7 +413,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if params[:ref].present?
@ref = params[:ref]
- @commit = @target_project.commit(@ref)
+ @commit = @target_project.commit("refs/heads/#{@ref}")
end
render layout: false
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index be52b0fa7cf..5922e686cd0 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -13,11 +13,14 @@ class Projects::MilestonesController < Projects::ApplicationController
def index
@milestones =
case params[:state]
- when 'all' then @project.milestones.reorder(due_date: :desc, title: :asc)
- when 'closed' then @project.milestones.closed.reorder(due_date: :desc, title: :asc)
- else @project.milestones.active.reorder(due_date: :asc, title: :asc)
+ when 'all' then @project.milestones
+ when 'closed' then @project.milestones.closed
+ else @project.milestones.active
end
+ @sort = params[:sort] || 'due_date_asc'
+ @milestones = @milestones.sort(@sort)
+
@milestones = @milestones.includes(:project)
respond_to do |format|
format.html do
diff --git a/app/controllers/projects/settings/members_controller.rb b/app/controllers/projects/settings/members_controller.rb
index cbfa2afa959..54f9dceddef 100644
--- a/app/controllers/projects/settings/members_controller.rb
+++ b/app/controllers/projects/settings/members_controller.rb
@@ -9,6 +9,7 @@ module Projects
@skip_groups = @group_links.pluck(:group_id)
@skip_groups << @project.namespace_id unless @project.personal?
+ @skip_groups += @project.group.ancestors.pluck(:id) if @project.group
@project_members = MembersFinder.new(@project, current_user).execute
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 4f094146348..637b61504d8 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -34,6 +34,7 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir
return render_404 unless @commit_params.values.all?
+ update_ref
create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
success_path: namespace_project_tree_path(@project.namespace, @project, File.join(@target_branch, @dir_name)),
failure_path: namespace_project_tree_path(@project.namespace, @project, @ref))
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index b44f38d4a0c..a49a1f50a81 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -1,5 +1,4 @@
class RegistrationsController < Devise::RegistrationsController
- before_action :signup_enabled?
include Recaptcha::Verify
def new
@@ -21,6 +20,8 @@ class RegistrationsController < Devise::RegistrationsController
flash.delete :recaptcha_error
render action: 'new'
end
+ rescue Gitlab::Access::AccessDeniedError
+ redirect_to(new_user_session_path)
end
def destroy
@@ -50,12 +51,6 @@ class RegistrationsController < Devise::RegistrationsController
private
- def signup_enabled?
- unless current_application_settings.signup_enabled?
- redirect_to(new_user_session_path)
- end
- end
-
def sign_up_params
params.require(:user).permit(:username, :email, :email_confirmation, :name, :password)
end
@@ -65,7 +60,7 @@ class RegistrationsController < Devise::RegistrationsController
end
def resource
- @resource ||= User.new(sign_up_params)
+ @resource ||= Users::CreateService.new(current_user, sign_up_params).build
end
def devise_mapping
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 6e29f1e8a65..2683614d2e8 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -39,7 +39,7 @@ class UsersController < ApplicationController
format.html { render 'show' }
format.json do
render json: {
- html: view_to_html_string("shared/projects/_list", projects: @projects, remote: true)
+ html: view_to_html_string("shared/projects/_list", projects: @projects)
}
end
end
@@ -65,7 +65,7 @@ class UsersController < ApplicationController
format.html { render 'show' }
format.json do
render json: {
- html: view_to_html_string("snippets/_snippets", collection: @snippets, remote: true)
+ html: view_to_html_string("snippets/_snippets", collection: @snippets)
}
end
end
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index 13d33a1c31b..b7f091f334d 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -24,7 +24,6 @@ class TodosFinder
def execute
items = current_user.todos
- items = include_associations(items)
items = by_action_id(items)
items = by_action(items)
items = by_author(items)
@@ -39,17 +38,6 @@ class TodosFinder
private
- def include_associations(items)
- return items unless params[:include_associations]
-
- items.includes(
- [
- target: { project: [:route, namespace: :route] },
- author: { namespace: :route },
- ]
- )
- end
-
def action_id?
action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 5053b937c02..bd3f51fc658 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -89,10 +89,12 @@ module MilestonesHelper
content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" }
content.slice!("about ")
content << " remaining"
+ content.html_safe
elsif milestone.start_date && milestone.start_date.past?
days = milestone.elapsed_days
content = content_tag(:strong, days)
content << " #{'day'.pluralize(days)} elapsed"
+ content.html_safe
end
end
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index 2e3a15bc1b9..7f656b8caae 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -6,7 +6,13 @@ module NamespacesHelper
def namespaces_options(selected = :current_user, display_path: false, extra_group: nil)
groups = current_user.owned_groups + current_user.masters_groups
- groups << extra_group if extra_group && !Group.exists?(name: extra_group.name)
+ unless extra_group.nil? || extra_group.is_a?(Group)
+ extra_group = Group.find(extra_group) if Namespace.find(extra_group).kind == 'group'
+ end
+
+ if extra_group && extra_group.is_a?(Group) && (!Group.exists?(name: extra_group.name) || Ability.allowed?(current_user, :read_group, extra_group))
+ groups |= [extra_group]
+ end
users = [current_user.namespace]
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index a8f167cbff2..991fd949b94 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -31,7 +31,11 @@ module NavHelper
end
def layout_nav_class
- "page-with-layout-nav" if defined?(nav) && nav
+ class_name = ''
+ class_name << " page-with-layout-nav" if defined?(nav) && nav
+ class_name << " page-with-sub-nav" if content_for?(:sub_nav)
+
+ class_name
end
def nav_control_class
diff --git a/app/helpers/sorting_helper.rb b/app/helpers/sorting_helper.rb
index 959ee310867..5c89cbea3fc 100644
--- a/app/helpers/sorting_helper.rb
+++ b/app/helpers/sorting_helper.rb
@@ -2,6 +2,7 @@ module SortingHelper
def sort_options_hash
{
sort_value_name => sort_title_name,
+ sort_value_name_desc => sort_title_name_desc,
sort_value_recently_updated => sort_title_recently_updated,
sort_value_oldest_updated => sort_title_oldest_updated,
sort_value_recently_created => sort_title_recently_created,
@@ -50,6 +51,17 @@ module SortingHelper
}
end
+ def milestone_sort_options_hash
+ {
+ sort_value_name => sort_title_name_asc,
+ sort_value_name_desc => sort_title_name_desc,
+ sort_value_due_date_soon => sort_title_due_date_soon,
+ sort_value_due_date_later => sort_title_due_date_later,
+ sort_value_start_date_soon => sort_title_start_date_soon,
+ sort_value_start_date_later => sort_title_start_date_later,
+ }
+ end
+
def sort_title_priority
'Priority'
end
@@ -90,6 +102,14 @@ module SortingHelper
'Due later'
end
+ def sort_title_start_date_soon
+ 'Start soon'
+ end
+
+ def sort_title_start_date_later
+ 'Start later'
+ end
+
def sort_title_name
'Name'
end
@@ -202,6 +222,14 @@ module SortingHelper
'due_date_desc'
end
+ def sort_value_start_date_soon
+ 'start_date_asc'
+ end
+
+ def sort_value_start_date_later
+ 'start_date_desc'
+ end
+
def sort_value_name
'name_asc'
end
diff --git a/app/helpers/todos_helper.rb b/app/helpers/todos_helper.rb
index 847a8fdfca6..4f5adf623f2 100644
--- a/app/helpers/todos_helper.rb
+++ b/app/helpers/todos_helper.rb
@@ -39,13 +39,9 @@ module TodosHelper
namespace_project_commit_path(todo.project.namespace.becomes(Namespace), todo.project,
todo.target, anchor: anchor)
else
- if todo.build_failed?
- # associated namespace and route would be loaded from the db again if todo.project was used
- project = todo.target.project
- path = [:pipelines, project.namespace.becomes(Namespace), project, todo.target]
- else
- path = [todo.target]
- end
+ path = [todo.project.namespace.becomes(Namespace), todo.project, todo.target]
+
+ path.unshift(:pipelines) if todo.build_failed?
polymorphic_path(path, anchor: anchor)
end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
new file mode 100644
index 00000000000..9c623c9ba7c
--- /dev/null
+++ b/app/helpers/users_helper.rb
@@ -0,0 +1,7 @@
+module UsersHelper
+ def user_link(user)
+ link_to(user.name, user_path(user),
+ title: user.email,
+ class: 'has-tooltip commit-committer-link')
+ end
+end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index e7bd20b322a..4d54426b79e 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -50,6 +50,7 @@ module Issuable
:email,
:public_email,
to: :author,
+ allow_nil: true,
prefix: true
delegate :name,
diff --git a/app/models/concerns/spammable.rb b/app/models/concerns/spammable.rb
index 107e6764ba2..647a6cad3d7 100644
--- a/app/models/concerns/spammable.rb
+++ b/app/models/concerns/spammable.rb
@@ -41,7 +41,7 @@ module Spammable
def check_for_spam
error_msg = if Gitlab::Recaptcha.enabled?
"Your #{spammable_entity_type} has been recognized as spam. "\
- "You can still submit it by solving Captcha."
+ "Please, change the content or solve the reCAPTCHA to proceed."
else
"Your #{spammable_entity_type} has been recognized as spam and has been discarded."
end
diff --git a/app/models/group.rb b/app/models/group.rb
index bd0ecae3da4..60274386103 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -207,7 +207,7 @@ class Group < Namespace
end
def members_with_parents
- GroupMember.non_request.where(source_id: ancestors.map(&:id).push(id))
+ GroupMember.non_request.where(source_id: ancestors.pluck(:id).push(id))
end
def users_with_parents
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 602eed86d9e..10a5d9d2a24 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -211,9 +211,8 @@ class Issue < ActiveRecord::Base
due_date.try(:past?) || false
end
- # Only issues on public projects should be checked for spam
def check_for_spam?
- project.public?
+ project.public? && (title_changed? || description_changed?)
end
def as_json(options = {})
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index cef8ad76b07..5ff83944d8c 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -7,7 +7,6 @@ class MergeRequest < ActiveRecord::Base
belongs_to :target_project, class_name: "Project"
belongs_to :source_project, class_name: "Project"
- belongs_to :project, foreign_key: :target_project_id
belongs_to :merge_user, class_name: "User"
has_many :merge_request_diffs, dependent: :destroy
@@ -155,8 +154,10 @@ class MergeRequest < ActiveRecord::Base
#
# Returns an ActiveRecord::Relation.
def self.in_projects(relation)
- source = where(source_project_id: relation).select(:id)
- target = where(target_project_id: relation).select(:id)
+ # unscoping unnecessary conditions that'll be applied
+ # when executing `where("merge_requests.id IN (#{union.to_sql})")`
+ source = unscoped.where(source_project_id: relation).select(:id)
+ target = unscoped.where(target_project_id: relation).select(:id)
union = Gitlab::SQL::Union.new([source, target])
where("merge_requests.id IN (#{union.to_sql})")
@@ -541,6 +542,10 @@ class MergeRequest < ActiveRecord::Base
target_project != source_project
end
+ def project
+ target_project
+ end
+
# If the merge request closes any issues, save this information in the
# `MergeRequestsClosingIssues` model. This is a performance optimization.
# Calculating this information for a number of merge requests requires
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index c0deb59ec4c..e85d5709624 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -107,6 +107,21 @@ class Milestone < ActiveRecord::Base
end
end
+ def self.sort(method)
+ case method.to_s
+ when 'due_date_asc'
+ reorder(Gitlab::Database.nulls_last_order('due_date', 'ASC'))
+ when 'due_date_desc'
+ reorder(Gitlab::Database.nulls_last_order('due_date', 'DESC'))
+ when 'start_date_asc'
+ reorder(Gitlab::Database.nulls_last_order('start_date', 'ASC'))
+ when 'start_date_desc'
+ reorder(Gitlab::Database.nulls_last_order('start_date', 'DESC'))
+ else
+ order_by(method)
+ end
+ end
+
##
# Returns the String necessary to reference this Milestone in Markdown
#
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index ac03f098908..1e0bbe69eb1 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -195,7 +195,7 @@ class Namespace < ActiveRecord::Base
# Scopes the model on direct and indirect children of the record
def descendants
- self.class.joins(:route).where('routes.path LIKE ?', "#{route.path}/%").reorder('routes.path ASC')
+ self.class.joins(:route).merge(Route.inside_path(route.path)).reorder('routes.path ASC')
end
def user_ids_for_project_authorizations
diff --git a/app/models/project.rb b/app/models/project.rb
index a579ac7dc64..649d0e100c3 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -239,7 +239,7 @@ class Project < ActiveRecord::Base
# We need routes alias rs for JOIN so it does not conflict with
# includes(:route) which we use in ProjectsFinder.
joins("INNER JOIN routes rs ON rs.source_id = projects.id AND rs.source_type = 'Project'").
- where('rs.path LIKE ?', "#{path}/%")
+ where('rs.path LIKE ?', "#{sanitize_sql_like(path)}/%")
end
# "enabled" here means "not disabled". It includes private features!
@@ -315,20 +315,15 @@ class Project < ActiveRecord::Base
ntable = Namespace.arel_table
pattern = "%#{query}%"
- projects = select(:id).where(
+ # unscoping unnecessary conditions that'll be applied
+ # when executing `where("projects.id IN (#{union.to_sql})")`
+ projects = unscoped.select(:id).where(
ptable[:path].matches(pattern).
or(ptable[:name].matches(pattern)).
or(ptable[:description].matches(pattern))
)
- # We explicitly remove any eager loading clauses as they're:
- #
- # 1. Not needed by this query
- # 2. Combined with .joins(:namespace) lead to all columns from the
- # projects & namespaces tables being selected, leading to a SQL error
- # due to the columns of all UNION'd queries no longer being the same.
- namespaces = select(:id).
- except(:includes).
+ namespaces = unscoped.select(:id).
joins(:namespace).
where(ntable[:name].matches(pattern))
diff --git a/app/models/project_services/chat_notification_service.rb b/app/models/project_services/chat_notification_service.rb
index 200be99f36b..75834103db5 100644
--- a/app/models/project_services/chat_notification_service.rb
+++ b/app/models/project_services/chat_notification_service.rb
@@ -6,7 +6,7 @@ class ChatNotificationService < Service
default_value_for :category, 'chat'
prop_accessor :webhook, :username, :channel
- boolean_accessor :notify_only_broken_pipelines
+ boolean_accessor :notify_only_broken_pipelines, :notify_only_default_branch
validates :webhook, presence: true, url: true, if: :activated?
@@ -17,6 +17,7 @@ class ChatNotificationService < Service
if properties.nil?
self.properties = {}
self.notify_only_broken_pipelines = true
+ self.notify_only_default_branch = true
end
end
@@ -29,6 +30,19 @@ class ChatNotificationService < Service
pipeline wiki_page]
end
+ def fields
+ default_fields + build_event_channels
+ end
+
+ def default_fields
+ [
+ { type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
+ { type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
+ { type: 'checkbox', name: 'notify_only_broken_pipelines' },
+ { type: 'checkbox', name: 'notify_only_default_branch' },
+ ]
+ end
+
def execute(data)
return unless supported_events.include?(data[:object_kind])
return unless webhook.present?
@@ -123,6 +137,17 @@ class ChatNotificationService < Service
end
def should_pipeline_be_notified?(data)
+ notify_for_ref?(data) && notify_for_pipeline?(data)
+ end
+
+ def notify_for_ref?(data)
+ return true if data[:object_attributes][:tag]
+ return true unless notify_only_default_branch
+
+ data[:object_attributes][:ref] == project.default_branch
+ end
+
+ def notify_for_pipeline?(data)
case data[:object_attributes][:status]
when 'success'
!notify_only_broken_pipelines?
diff --git a/app/models/project_services/mattermost_service.rb b/app/models/project_services/mattermost_service.rb
index 1156d050622..0362ed172c7 100644
--- a/app/models/project_services/mattermost_service.rb
+++ b/app/models/project_services/mattermost_service.rb
@@ -22,19 +22,11 @@ class MattermostService < ChatNotificationService
</ol>'
end
- def fields
- default_fields + build_event_channels
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: 'e.g. http://mattermost_host/hooks/…' },
- { type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
- { type: 'checkbox', name: 'notify_only_broken_pipelines' },
- ]
- end
-
def default_channel_placeholder
"Channel handle (e.g. town-square)"
end
+
+ def webhook_placeholder
+ 'http://mattermost.example.com/hooks/…'
+ end
end
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 375966b9efc..5cff9a42484 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -30,7 +30,14 @@ class PrometheusService < MonitoringService
end
def help
- 'Retrieves `container_cpu_usage_seconds_total` and `container_memory_usage_bytes` from the configured Prometheus server. An `environment` label is required on each metric to identify the Environment.'
+ <<-MD.strip_heredoc
+ Retrieves the Kubernetes node metrics `container_cpu_usage_seconds_total`
+ and `container_memory_usage_bytes` from the configured Prometheus server.
+
+ If you are not using [Auto-Deploy](https://docs.gitlab.com/ee/ci/autodeploy/index.html)
+ or have set up your own Prometheus server, an `environment` label is required on each metric to
+ [identify the Environment](https://docs.gitlab.com/ce/user/project/integrations/prometheus.html#metrics-and-labels).
+ MD
end
def self.to_param
@@ -67,16 +74,16 @@ class PrometheusService < MonitoringService
def calculate_reactive_cache(environment_slug)
return unless active? && project && !project.pending_delete?
- memory_query = %{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024}
- cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))}
+ memory_query = %{(sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024}
+ cpu_query = %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100}
{
success: true,
metrics: {
- # Memory used in MB
+ # Average Memory used in MB
memory_values: client.query_range(memory_query, start: 8.hours.ago),
memory_current: client.query(memory_query),
- # CPU Usage rate in cores.
+ # Average CPU Utilization
cpu_values: client.query_range(cpu_query, start: 8.hours.ago),
cpu_current: client.query(cpu_query)
},
diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb
index b657db6f9ee..71da0af75f6 100644
--- a/app/models/project_services/slack_service.rb
+++ b/app/models/project_services/slack_service.rb
@@ -21,19 +21,11 @@ class SlackService < ChatNotificationService
</ol>'
end
- def fields
- default_fields + build_event_channels
- end
-
- def default_fields
- [
- { type: 'text', name: 'webhook', placeholder: 'e.g. https://hooks.slack.com/services/…' },
- { type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
- { type: 'checkbox', name: 'notify_only_broken_pipelines' },
- ]
- end
-
def default_channel_placeholder
"Channel name (e.g. general)"
end
+
+ def webhook_placeholder
+ 'https://hooks.slack.com/services/…'
+ end
end
diff --git a/app/models/route.rb b/app/models/route.rb
index 41e6eb7cb73..4b3efab5c3c 100644
--- a/app/models/route.rb
+++ b/app/models/route.rb
@@ -10,9 +10,11 @@ class Route < ActiveRecord::Base
after_update :rename_descendants
+ scope :inside_path, -> (path) { where('routes.path LIKE ?', "#{sanitize_sql_like(path)}/%") }
+
def rename_descendants
if path_changed? || name_changed?
- descendants = Route.where('path LIKE ?', "#{path_was}/%")
+ descendants = self.class.inside_path(path_was)
descendants.each do |route|
attributes = {}
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index dbd564e5e7d..30aca62499c 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -132,7 +132,8 @@ class Snippet < ActiveRecord::Base
end
def check_for_spam?
- public?
+ visibility_level_changed?(to: Snippet::PUBLIC) ||
+ (public? && (title_changed? || content_changed?))
end
def spammable_entity_type
diff --git a/app/models/user.rb b/app/models/user.rb
index 8c7ad5d5174..612066654dc 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -115,7 +115,9 @@ class User < ActiveRecord::Base
validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
validates :bio, length: { maximum: 255 }, allow_blank: true
- validates :projects_limit, presence: true, numericality: { greater_than_or_equal_to: 0 }
+ validates :projects_limit,
+ presence: true,
+ numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: Gitlab::Database::MAX_INT_VALUE }
validates :username,
namespace: true,
presence: true,
@@ -126,10 +128,9 @@ class User < ActiveRecord::Base
validate :unique_email, if: ->(user) { user.email_changed? }
validate :owns_notification_email, if: ->(user) { user.notification_email_changed? }
validate :owns_public_email, if: ->(user) { user.public_email_changed? }
+ validate :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
- before_validation :generate_password, on: :create
- before_validation :signup_domain_valid?, on: :create, if: ->(user) { !user.created_by_id }
before_validation :sanitize_attrs
before_validation :set_notification_email, if: ->(user) { user.email_changed? }
before_validation :set_public_email, if: ->(user) { user.public_email_changed? }
@@ -139,8 +140,6 @@ class User < ActiveRecord::Base
before_save :ensure_external_user_rights
after_save :ensure_namespace_correct
after_initialize :set_projects_limit
- before_create :check_confirmation_email
- after_create :post_create_hook
after_destroy :post_destroy_hook
# User's Layout preference
@@ -324,6 +323,8 @@ class User < ActiveRecord::Base
end
def find_by_personal_access_token(token_string)
+ return unless token_string
+
PersonalAccessTokensFinder.new(state: 'active').find_by(token: token_string)&.user
end
@@ -382,10 +383,8 @@ class User < ActiveRecord::Base
"#{self.class.reference_prefix}#{username}"
end
- def generate_password
- if force_random_password
- self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min)
- end
+ def skip_confirmation=(bool)
+ skip_confirmation! if bool
end
def generate_reset_token
@@ -397,10 +396,6 @@ class User < ActiveRecord::Base
@reset_token
end
- def check_confirmation_email
- skip_confirmation! unless current_application_settings.send_user_confirmation_email
- end
-
def recently_sent_password_reset?
reset_password_sent_at.present? && reset_password_sent_at >= 1.minute.ago
end
@@ -795,12 +790,6 @@ class User < ActiveRecord::Base
end
end
- def post_create_hook
- log_info("User \"#{name}\" (#{email}) was created")
- notification_service.new_user(self, @reset_token) if created_by_id
- system_hook_service.execute_hooks_for(self, :create)
- end
-
def post_destroy_hook
log_info("User \"#{name}\" (#{email}) was removed")
system_hook_service.execute_hooks_for(self, :destroy)
diff --git a/app/services/boards/issues/list_service.rb b/app/services/boards/issues/list_service.rb
index 83f51947bd4..cb6d30396ec 100644
--- a/app/services/boards/issues/list_service.rb
+++ b/app/services/boards/issues/list_service.rb
@@ -3,7 +3,7 @@ module Boards
class ListService < BaseService
def execute
issues = IssuesFinder.new(current_user, filter_params).execute
- issues = without_board_labels(issues) unless movable_list?
+ issues = without_board_labels(issues) unless list
issues = with_list_label(issues) if movable_list?
issues.order_by_position_and_priority
end
diff --git a/app/services/create_branch_service.rb b/app/services/create_branch_service.rb
index b07338d500a..673ed02f952 100644
--- a/app/services/create_branch_service.rb
+++ b/app/services/create_branch_service.rb
@@ -25,12 +25,12 @@ class CreateBranchService < BaseService
private
def create_master_branch
- project.repository.commit_file(
+ project.repository.create_file(
current_user,
'/README.md',
'',
message: 'Add README.md',
- branch_name: 'master',
- update: false)
+ branch_name: 'master'
+ )
end
end
diff --git a/app/services/spam_check_service.rb b/app/services/spam_check_service.rb
index 023e0824e85..11030bee8f1 100644
--- a/app/services/spam_check_service.rb
+++ b/app/services/spam_check_service.rb
@@ -14,6 +14,9 @@ module SpamCheckService
@spam_log_id = params.delete(:spam_log_id)
end
+ # In order to be proceed to the spam check process, @spammable has to be
+ # a dirty instance, which means it should be already assigned with the new
+ # attribute values.
def spam_check(spammable, user)
spam_service = SpamService.new(spammable, @request)
diff --git a/app/services/users/create_service.rb b/app/services/users/create_service.rb
new file mode 100644
index 00000000000..f4f0b80f30a
--- /dev/null
+++ b/app/services/users/create_service.rb
@@ -0,0 +1,110 @@
+module Users
+ # Service for creating a new user.
+ class CreateService < BaseService
+ def initialize(current_user, params = {})
+ @current_user = current_user
+ @params = params.dup
+ end
+
+ def build
+ raise Gitlab::Access::AccessDeniedError unless can_create_user?
+
+ user = User.new(build_user_params)
+
+ if current_user&.is_admin?
+ if params[:reset_password]
+ @reset_token = user.generate_reset_token
+ params[:force_random_password] = true
+ end
+
+ if params[:force_random_password]
+ random_password = Devise.friendly_token.first(Devise.password_length.min)
+ user.password = user.password_confirmation = random_password
+ end
+ end
+
+ identity_attrs = params.slice(:extern_uid, :provider)
+
+ if identity_attrs.any?
+ user.identities.build(identity_attrs)
+ end
+
+ user
+ end
+
+ def execute
+ user = build
+
+ if user.save
+ log_info("User \"#{user.name}\" (#{user.email}) was created")
+ notification_service.new_user(user, @reset_token) if @reset_token
+ system_hook_service.execute_hooks_for(user, :create)
+ end
+
+ user
+ end
+
+ private
+
+ def can_create_user?
+ (current_user.nil? && current_application_settings.signup_enabled?) || current_user&.is_admin?
+ end
+
+ # Allowed params for creating a user (admins only)
+ def admin_create_params
+ [
+ :access_level,
+ :admin,
+ :avatar,
+ :bio,
+ :can_create_group,
+ :color_scheme_id,
+ :email,
+ :external,
+ :force_random_password,
+ :hide_no_password,
+ :hide_no_ssh_key,
+ :key_id,
+ :linkedin,
+ :name,
+ :password,
+ :password_expires_at,
+ :projects_limit,
+ :remember_me,
+ :skip_confirmation,
+ :skype,
+ :theme_id,
+ :twitter,
+ :username,
+ :website_url
+ ]
+ end
+
+ # Allowed params for user signup
+ def signup_params
+ [
+ :email,
+ :email_confirmation,
+ :name,
+ :password,
+ :username
+ ]
+ end
+
+ def build_user_params
+ if current_user&.is_admin?
+ user_params = params.slice(*admin_create_params)
+ user_params[:created_by_id] = current_user.id
+
+ if params[:reset_password]
+ user_params.merge!(force_random_password: true, password_expires_at: nil)
+ end
+ else
+ user_params = params.slice(*signup_params)
+ user_params[:skip_confirmation] = !current_application_settings.send_user_confirmation_email
+ end
+
+ user_params
+ end
+ end
+end
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index e67ad663720..ebca9beb035 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -43,28 +43,34 @@
%h4
Features
%hr
- %p
- Sign up
+ - sign_up = "Sign up"
+ %p{ "aria-label" => "#{sign_up}: status " + (signup_enabled? ? "on" : "off") }
+ = sign_up
%span.light.pull-right
= boolean_to_icon signup_enabled?
- %p
- LDAP
+ - ldap = "LDAP"
+ %p{ "aria-label" => "#{ldap}: status " + (Gitlab.config.ldap.enabled ? "on" : "off") }
+ = ldap
%span.light.pull-right
= boolean_to_icon Gitlab.config.ldap.enabled
- %p
- Gravatar
+ - gravatar = "Gravatar"
+ %p{ "aria-label" => "#{gravatar}: status " + (gravatar_enabled? ? "on" : "off") }
+ = gravatar
%span.light.pull-right
= boolean_to_icon gravatar_enabled?
- %p
- OmniAuth
+ - omniauth = "OmniAuth"
+ %p{ "aria-label" => "#{omniauth}: status " + (Gitlab.config.omniauth.enabled ? "on" : "off") }
+ = omniauth
%span.light.pull-right
= boolean_to_icon Gitlab.config.omniauth.enabled
- %p
- Reply by email
+ - reply_email = "Reply by email"
+ %p{ "aria-label" => "#{reply_email}: status " + (Gitlab::IncomingEmail.enabled? ? "on" : "off") }
+ = reply_email
%span.light.pull-right
= boolean_to_icon Gitlab::IncomingEmail.enabled?
- %p
- Container Registry
+ - container_reg = "Container Registry"
+ %p{ "aria-label" => "#{container_reg}: status " + (Gitlab.config.registry.enabled ? "on" : "off") }
+ = container_reg
%span.light.pull-right
= boolean_to_icon Gitlab.config.registry.enabled
diff --git a/app/views/admin/users/_access_levels.html.haml b/app/views/admin/users/_access_levels.html.haml
index 7855239dfe5..794aaec89bd 100644
--- a/app/views/admin/users/_access_levels.html.haml
+++ b/app/views/admin/users/_access_levels.html.haml
@@ -2,7 +2,7 @@
%legend Access
.form-group
= f.label :projects_limit, class: 'control-label'
- .col-sm-10= f.number_field :projects_limit, min: 0, class: 'form-control'
+ .col-sm-10= f.number_field :projects_limit, min: 0, max: Gitlab::Database::MAX_INT_VALUE, class: 'form-control'
.form-group
= f.label :can_create_group, class: 'control-label'
diff --git a/app/views/award_emoji/_awards_block.html.haml b/app/views/award_emoji/_awards_block.html.haml
index a1ef34dc588..5aae410a63f 100644
--- a/app/views/award_emoji/_awards_block.html.haml
+++ b/app/views/award_emoji/_awards_block.html.haml
@@ -10,8 +10,8 @@
- if current_user
.award-menu-holder.js-award-holder
- %button.btn.award-control.js-add-award{ type: "button" }
+ %button.btn.award-control.has-tooltip.js-add-award{ type: 'button',
+ 'aria-label': 'Add emoji',
+ data: { title: 'Add emoji', placement: "bottom" } }
= icon('smile-o', class: "award-control-icon award-control-icon-normal")
= icon('spinner spin', class: "award-control-icon award-control-icon-loading")
- %span.award-control-text
- Add
diff --git a/app/views/ci/status/_graph_badge.html.haml b/app/views/ci/status/_graph_badge.html.haml
index 0530d21a7e2..128b418090f 100644
--- a/app/views/ci/status/_graph_badge.html.haml
+++ b/app/views/ci/status/_graph_badge.html.haml
@@ -6,7 +6,7 @@
- tooltip = "#{subject.name} - #{status.label}"
- if status.has_details?
- = link_to status.details_path, class: 'build-content has-tooltip', data: { toggle: 'tooltip', title: tooltip } do
+ = link_to status.details_path, class: 'build-content has-tooltip', data: { toggle: 'tooltip', title: tooltip, container: 'body' } do
%span{ class: klass }= custom_icon(status.icon)
.ci-status-text= subject.name
- else
@@ -15,6 +15,6 @@
.ci-status-text= subject.name
- if status.has_action?
- = link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title } do
+ = link_to status.action_path, class: 'ci-action-icon-container has-tooltip', method: status.action_method, data: { toggle: 'tooltip', title: status.action_title, container: 'body' } do
%i.ci-action-icon-wrapper{ class: "js-#{status.action_icon.dasherize}" }
= custom_icon(status.action_icon)
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index d31ced004a0..52d6ebd8a14 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -19,12 +19,13 @@
.nav-controls
- if @todos.any?(&:pending?)
- = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete, data: { href: destroy_all_dashboard_todos_path(todos_filter_params) } do
- Mark all as done
- = icon('spinner spin')
- = link_to bulk_restore_dashboard_todos_path, class: 'btn btn-loading js-todos-undo-all hidden', method: :patch , data: { href: bulk_restore_dashboard_todos_path(todos_filter_params) } do
- Undo mark all as done
- = icon('spinner spin')
+ .append-right-default
+ = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'btn btn-loading js-todos-mark-all', method: :delete, data: { href: destroy_all_dashboard_todos_path(todos_filter_params) } do
+ Mark all as done
+ = icon('spinner spin')
+ = link_to bulk_restore_dashboard_todos_path, class: 'btn btn-loading js-todos-undo-all hidden', method: :patch , data: { href: bulk_restore_dashboard_todos_path(todos_filter_params) } do
+ Undo mark all as done
+ = icon('spinner spin')
.todos-filters
.row-content-block.second-block
@@ -67,12 +68,11 @@
= link_to todos_filter_path(sort: sort_value_oldest_created) do
= sort_title_oldest_created
-
.js-todos-all
- if @todos.any?
.js-todos-list-container
.js-todos-options{ data: { per_page: @todos.limit_value, current_page: @todos.current_page, total_pages: @todos.total_pages } }
- .panel.panel-default.panel-small.panel-without-border
+ .panel.panel-default.panel-without-border.panel-without-margin
%ul.content-list.todos-list
= render @todos
= paginate @todos, theme: "gitlab"
diff --git a/app/views/devise/sessions/_new_base.html.haml b/app/views/devise/sessions/_new_base.html.haml
index 5d359538efe..21c751a23f8 100644
--- a/app/views/devise/sessions/_new_base.html.haml
+++ b/app/views/devise/sessions/_new_base.html.haml
@@ -8,7 +8,7 @@
- if devise_mapping.rememberable?
.remember-me.checkbox
%label{ for: "user_remember_me" }
- = f.check_box :remember_me
+ = f.check_box :remember_me, class: 'remember-me-checkbox'
%span Remember me
.pull-right.forgot-password
= link_to "Forgot your password?", new_password_path(resource_name)
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 56f463572bb..f630f1effdc 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -17,24 +17,3 @@
= link_to filter_projects_path(visibility_level: level) do
= visibility_level_icon(level)
= visibility_level_label(level)
-
-- if @tags.present?
- .dropdown
- %button.dropdown-toggle{ href: '#', "data-toggle" => "dropdown" }
- = icon('tags')
- %span.light Tags:
- - if params[:tag].present?
- = params[:tag]
- - else
- Any
- = icon('chevron-down')
- %ul.dropdown-menu.dropdown-menu-align-right
- %li
- = link_to filter_projects_path(tag: nil) do
- Any
-
- - @tags.each do |tag|
- %li{ class: active_when(tag.name == params[:tag]) || 'light' }
- = link_to filter_projects_path(tag: tag.name) do
- = icon('tag')
- = tag.name
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 2684f16c373..8e6da3fad90 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -118,6 +118,12 @@
.key m
%td
Go to merge requests
+ %tr
+ %td.shortcut
+ .key g
+ .key t
+ %td
+ Go to todos
%tbody
%tr
%th
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index a35a918d501..b7df11681d3 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -3,8 +3,9 @@
.layout-nav
.container-fluid
= render "layouts/nav/#{nav}"
- .content-wrapper{ class: "#{layout_nav_class}" }
+ - if content_for?(:sub_nav)
= yield :sub_nav
+ .content-wrapper{ class: layout_nav_class }
.alert-wrapper
= render "layouts/broadcast"
= render "layouts/flash"
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 5fde5c2613e..7ddee0e5244 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -32,7 +32,7 @@
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
%li
- = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = link_to dashboard_todos_path, title: 'Todos', aria: { label: "Todos" }, class: 'shortcuts-todos', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('bell fw')
%span.badge.todos-pending-count{ class: ("hidden" if todos_pending_count == 0) }
= todos_count_format(todos_pending_count)
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index df0a0212f3d..99690e6b98a 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -6,7 +6,9 @@
%h4.prepend-top-0
Syntax highlighting theme
%p
- This setting allow you to customize the appearance of the syntax.
+ This setting allows you to customize the appearance of the syntax.
+ = succeed '.' do
+ = link_to 'Learn more', help_page_path('user/profile/preferences', anchor: 'syntax-highlighting-theme'), target: '_blank'
.col-lg-9.syntax-theme
- Gitlab::ColorSchemes.each do |scheme|
= label_tag do
@@ -20,6 +22,8 @@
Behavior
%p
This setting allows you to customize the behavior of the system layout and default views.
+ = succeed '.' do
+ = link_to 'Learn more', help_page_path('user/profile/preferences', anchor: 'behavior'), target: '_blank'
.col-lg-9
.form-group
= f.label :layout, class: 'label-light' do
@@ -29,13 +33,11 @@
Choose between fixed (max. 1200px) and fluid (100%) application layout.
.form-group
= f.label :dashboard, class: 'label-light' do
- Default Dashboard
- = link_to('(?)', help_page_path('profile/preferences') + '#default-dashboard', target: '_blank')
+ Default dashboard
= f.select :dashboard, dashboard_choices, {}, class: 'form-control'
.form-group
= f.label :project_view, class: 'label-light' do
Project view
- = link_to('(?)', help_page_path('profile/preferences') + '#default-project-view', target: '_blank')
= f.select :project_view, project_view_choices, {}, class: 'form-control'
.help-block
Choose what content you want to see on a project's home page.
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index 4924c73cf8e..e14885f264b 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -5,7 +5,7 @@
%a.close{ href: "#", "data-dismiss" => "modal" } ×
%h3.page-title= title
.modal-body
- = form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal' do
+ = form_tag form_path, method: method, class: 'js-quick-submit js-upload-blob-form form-horizontal', data: { method: method } do
.dropzone
.dropzone-previews.blob-upload-dropzone-previews
%p.dz-message.light
@@ -24,8 +24,5 @@
.inline.prepend-left-10
= commit_in_fork_help
-
-:javascript
- gl.utils.disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
- new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
- new NewCommitForm($('.js-upload-blob-form'))
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('blob')
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 3bcddcb37f1..afe0b5dba45 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -2,7 +2,7 @@
- page_title "Edit", @blob.path, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
- = page_specific_javascript_bundle_tag('blob_edit')
+ = page_specific_javascript_bundle_tag('blob')
= render "projects/commits/head"
%div{ class: container_class }
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index e0ce8cc9601..4c449e040ee 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -1,7 +1,7 @@
- page_title "New File", @path.presence, @ref
- content_for :page_specific_javascripts do
= page_specific_javascript_tag('lib/ace.js')
- = page_specific_javascript_bundle_tag('blob_edit')
+ = page_specific_javascript_bundle_tag('blob')
%h3.page-title
New File
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index 78720d88e4e..b597c7f7a12 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -1,6 +1,6 @@
- builds = @build.pipeline.builds.to_a
-%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "151", "spy" => "affix" } }
+%aside.right-sidebar.right-sidebar-expanded.build-sidebar.js-build-sidebar.js-right-sidebar{ data: { "offset-top" => "153", "spy" => "affix" } }
.block.build-sidebar-header.visible-xs-block.visible-sm-block.append-bottom-default
Job
%strong ##{@build.id}
@@ -137,3 +137,6 @@
= build.id
- if build.retried?
%i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' }
+
+:javascript
+ new Sidebar();
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 307010edb58..d5fe771613c 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -1,6 +1,6 @@
- @no_container = true
- page_title "#{@build.name} (##{@build.id})", "Jobs"
-= render "projects/pipelines/head", build_subnav: true
+= render "projects/pipelines/head"
%div{ class: container_class }
.build-page
diff --git a/app/views/projects/commit/_pipelines_list.haml b/app/views/projects/commit/_pipelines_list.haml
index da5a676274f..09e3a775d1c 100644
--- a/app/views/projects/commit/_pipelines_list.haml
+++ b/app/views/projects/commit/_pipelines_list.haml
@@ -1,6 +1,7 @@
- disable_initialization = local_assigns.fetch(:disable_initialization, false)
#commit-pipeline-table-view{ data: { disable_initialization: disable_initialization,
endpoint: endpoint,
+ "help-page-path" => help_page_path('ci/quick_start/README'),
} }
- content_for :page_specific_javascripts do
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 2802a4eca7b..82e0d0025ec 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -31,7 +31,7 @@
= f.select(:default_branch, @project.repository.branch_names, {}, {class: 'select2 select-wide'})
.form-group
= f.label :tag_list, "Tags", class: 'label-light'
- = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
+ = f.text_field :tag_list, value: @project.tag_list.sort.join(', '), maxlength: 2000, class: "form-control"
%p.help-block Separate tags with commas.
%hr
%fieldset
diff --git a/app/views/projects/environments/metrics.html.haml b/app/views/projects/environments/metrics.html.haml
index b8c1782f050..3b45162df52 100644
--- a/app/views/projects/environments/metrics.html.haml
+++ b/app/views/projects/environments/metrics.html.haml
@@ -18,7 +18,11 @@
= render 'projects/deployments/actions', deployment: @environment.last_deployment
.row
.col-sm-12
+ %h4
+ CPU utilization
%svg.prometheus-graph{ 'graph-type' => 'cpu_values' }
.row
.col-sm-12
+ %h4
+ Memory usage
%svg.prometheus-graph{ 'graph-type' => 'memory_values' }
diff --git a/app/views/projects/environments/terminal.html.haml b/app/views/projects/environments/terminal.html.haml
index ef0dd0eda3c..c8363087d6a 100644
--- a/app/views/projects/environments/terminal.html.haml
+++ b/app/views/projects/environments/terminal.html.haml
@@ -16,7 +16,7 @@
.col-sm-6
.nav-controls
- = link_to @environment.external_url, class: 'btn btn-default' do
+ = link_to @environment.external_url, class: 'btn btn-default', target: '_blank', rel: 'noopener noreferrer nofollow' do
= icon('external-link')
= render 'projects/deployments/actions', deployment: @environment.last_deployment
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index ad14b4e583e..8d134aaac67 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -21,7 +21,7 @@
selected: f.object.source_project_id
.merge-request-select.dropdown
= f.hidden_field :source_branch
- = dropdown_toggle local_assigns.fetch(f.object.source_branch, "Select source branch"), { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
+ = dropdown_toggle f.object.source_branch || "Select source branch", { toggle: "dropdown", field_name: "#{f.object_name}[source_branch]" }, { toggle_class: "js-compare-dropdown js-source-branch" }
.dropdown-menu.dropdown-menu-selectable.dropdown-source-branch
= dropdown_title("Select source branch")
= dropdown_filter("Search branches")
diff --git a/app/views/projects/merge_requests/merge.js.haml b/app/views/projects/merge_requests/merge.js.haml
index f0a23bec5e7..e632fc681cf 100644
--- a/app/views/projects/merge_requests/merge.js.haml
+++ b/app/views/projects/merge_requests/merge.js.haml
@@ -1,7 +1,8 @@
- case @status
- when :success
+ - remove_source_branch = params[:should_remove_source_branch] == '1' || @merge_request.remove_source_branch?
:plain
- merge_request_widget.mergeInProgress(#{params[:should_remove_source_branch] == '1'});
+ merge_request_widget.mergeInProgress(#{remove_source_branch});
- when :merge_when_pipeline_succeeds
:plain
$('.mr-widget-body').html("#{escape_javascript(render('projects/merge_requests/widget/open/merge_when_pipeline_succeeds'))}");
diff --git a/app/views/projects/milestones/index.html.haml b/app/views/projects/milestones/index.html.haml
index 918f5d161bb..b6340a00b29 100644
--- a/app/views/projects/milestones/index.html.haml
+++ b/app/views/projects/milestones/index.html.haml
@@ -7,6 +7,7 @@
= render 'shared/milestones_filter', counts: milestone_counts(@project.milestones)
.nav-controls
+ = render 'shared/milestones_sort_dropdown'
- if can?(current_user, :admin_milestone, @project)
= link_to new_namespace_project_milestone_path(@project.namespace, @project), class: 'btn btn-new', title: 'New Milestone' do
New Milestone
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index d129da943f8..34a1214a350 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -23,7 +23,7 @@
- if current_user.can_select_namespace?
.input-group-addon
= root_url
- = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1}
+ = f.select :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), {}, { class: 'select2 js-select-namespace', tabindex: 1}
- else
.input-group-addon.static-namespace
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 5552086bc50..6c0e6d48d6c 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -37,7 +37,7 @@
":can-resolve" => can_resolve,
":author-name" => "'#{j(note.author.name)}'",
"author-avatar" => note.author.avatar_url,
- ":note-truncated" => "'#{truncate(note.note, length: 17)}'",
+ ":note-truncated" => "'#{j(truncate(note.note, length: 17))}'",
":resolved-by" => "'#{j(note.resolved_by.try(:name))}'",
"v-show" => "#{can_resolve || note.resolved?}",
"inline-template" => true,
diff --git a/app/views/projects/pipelines/_head.html.haml b/app/views/projects/pipelines/_head.html.haml
index a5acb7ac4a5..b02fef638ff 100644
--- a/app/views/projects/pipelines/_head.html.haml
+++ b/app/views/projects/pipelines/_head.html.haml
@@ -1,7 +1,7 @@
= content_for :sub_nav do
.scrolling-tabs-container.sub-nav-scroll
= render 'shared/nav_scroll'
- .nav-links.sub-nav.scrolling-tabs{ class: ('build' if local_assigns.fetch(:build_subnav, false)) }
+ .nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) }
- if project_nav_tab? :pipelines
= nav_link(path: 'pipelines#index', controller: :pipelines) do
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 0605af4fcd3..4be9a1371ec 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -1,10 +1,12 @@
.page-content-header
.header-main-content
= render 'ci/status/badge', status: @pipeline.detailed_status(current_user)
- %strong Pipeline ##{@commit.pipelines.last.id}
- triggered #{time_ago_with_tooltip(@commit.authored_date)} by
- = author_avatar(@commit, size: 24)
- = commit_author_link(@commit)
+ %strong Pipeline ##{@pipeline.id}
+ triggered #{time_ago_with_tooltip(@pipeline.created_at)}
+ - if @pipeline.user
+ by
+ = user_avatar(user: @pipeline.user, size: 24)
+ = user_link(@pipeline.user)
.header-action-buttons
- if can?(current_user, :update_pipeline, @pipeline.project)
- if @pipeline.retryable?
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index 5d59ce06612..3d73284699f 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -2,53 +2,19 @@
- page_title "Pipelines"
= render "projects/pipelines/head"
-%div{ class: container_class }
- .top-area
- %ul.nav-links
- %li.js-pipelines-tab-all{ class: active_when(@scope.nil?) }>
- = link_to project_pipelines_path(@project) do
- All
- %span.badge.js-totalbuilds-count
- = number_with_delimiter(@pipelines_count)
-
- %li.js-pipelines-tab-pending{ class: active_when(@scope == 'pending') }>
- = link_to project_pipelines_path(@project, scope: :pending) do
- Pending
- %span.badge
- = number_with_delimiter(@pending_count)
-
- %li.js-pipelines-tab-running{ class: active_when(@scope == 'running') }>
- = link_to project_pipelines_path(@project, scope: :running) do
- Running
- %span.badge.js-running-count
- = number_with_delimiter(@running_count)
-
- %li.js-pipelines-tab-finished{ class: active_when(@scope == 'finished') }>
- = link_to project_pipelines_path(@project, scope: :finished) do
- Finished
- %span.badge
- = number_with_delimiter(@finished_count)
-
- %li.js-pipelines-tab-branches{ class: active_when(@scope == 'branches') }>
- = link_to project_pipelines_path(@project, scope: :branches) do
- Branches
-
- %li.js-pipelines-tab-tags{ class: active_when(@scope == 'tags') }>
- = link_to project_pipelines_path(@project, scope: :tags) do
- Tags
-
- .nav-controls
- - if can? current_user, :create_pipeline, @project
- = link_to new_namespace_project_pipeline_path(@project.namespace, @project), class: 'btn btn-create' do
- Run pipeline
-
- - unless @repository.gitlab_ci_yml
- = link_to 'Get started with Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
-
- = link_to ci_lint_path, class: 'btn btn-default' do
- %span CI Lint
- .content-list.pipelines{ data: { url: namespace_project_pipelines_path(@project.namespace, @project, format: :json) } }
- .vue-pipelines-index
+#pipelines-list-vue{ data: { endpoint: namespace_project_pipelines_path(@project.namespace, @project, format: :json),
+ "css-class" => container_class,
+ "help-page-path" => help_page_path('ci/quick_start/README'),
+ "new-pipeline-path" => new_namespace_project_pipeline_path(@project.namespace, @project),
+ "can-create-pipeline" => can?(current_user, :create_pipeline, @project).to_s,
+ "all-path" => project_pipelines_path(@project),
+ "pending-path" => project_pipelines_path(@project, scope: :pending),
+ "running-path" => project_pipelines_path(@project, scope: :running),
+ "finished-path" => project_pipelines_path(@project, scope: :finished),
+ "branches-path" => project_pipelines_path(@project, scope: :branches),
+ "tags-path" => project_pipelines_path(@project, scope: :tags),
+ "has-ci" => @repository.gitlab_ci_yml,
+ "ci-lint-path" => ci_lint_path } }
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('vue_pipelines')
diff --git a/app/views/projects/services/_index.html.haml b/app/views/projects/services/_index.html.haml
index 964133504e6..86d5a0ec7b8 100644
--- a/app/views/projects/services/_index.html.haml
+++ b/app/views/projects/services/_index.html.haml
@@ -18,7 +18,7 @@
%th Last edit
- @services.sort_by(&:title).each do |service|
%tr
- %td
+ %td{ "aria-label" => "#{service.title}: status " + (service.activated? ? "on" : "off") }
= boolean_to_icon service.activated?
%td
= link_to edit_namespace_project_service_path(@project.namespace, @project, service.to_param) do
diff --git a/app/views/projects/stage/_in_stage_group.html.haml b/app/views/projects/stage/_in_stage_group.html.haml
index 9c5eb501174..671a3ef481c 100644
--- a/app/views/projects/stage/_in_stage_group.html.haml
+++ b/app/views/projects/stage/_in_stage_group.html.haml
@@ -1,5 +1,5 @@
- group_status = CommitStatus.where(id: subject).status
-%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}" } }
+%button.dropdown-menu-toggle.build-content.has-tooltip{ type: 'button', data: { toggle: 'dropdown', title: "#{name} - #{group_status}", container: 'body' } }
%span{ class: "ci-status-icon ci-status-icon-#{group_status}" }
= ci_icon_for_status(group_status)
%span.ci-status-text
diff --git a/app/views/projects/wikis/_sidebar.html.haml b/app/views/projects/wikis/_sidebar.html.haml
index 8c582f747b3..713b758727e 100644
--- a/app/views/projects/wikis/_sidebar.html.haml
+++ b/app/views/projects/wikis/_sidebar.html.haml
@@ -1,4 +1,4 @@
-%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" } }
+%aside.right-sidebar.right-sidebar-expanded.wiki-sidebar.js-wiki-sidebar.js-right-sidebar{ data: { "offset-top" => "102", "spy" => "affix" } }
.block.wiki-sidebar-header.append-bottom-default
%a.gutter-toggle.pull-right.visible-xs-block.visible-sm-block.js-sidebar-wiki-toggle{ href: "#" }
= icon('angle-double-right')
@@ -19,3 +19,6 @@
More Pages
= render 'projects/wikis/new'
+
+:javascript
+ new Sidebar();
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index 57a0eaa919e..db2ac1e1d12 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -4,10 +4,10 @@
Open
%span.badge= counts[:opened]
%li{ class: milestone_class_for_state(params[:state], 'closed') }>
- = link_to milestones_filter_path(state: 'closed') do
+ = link_to milestones_filter_path(state: 'closed', sort: 'due_date_desc') do
Closed
%span.badge= counts[:closed]
%li{ class: milestone_class_for_state(params[:state], 'all') }>
- = link_to milestones_filter_path(state: 'all') do
+ = link_to milestones_filter_path(state: 'all', sort: 'due_date_desc') do
All
%span.badge= counts[:all]
diff --git a/app/views/shared/_milestones_sort_dropdown.html.haml b/app/views/shared/_milestones_sort_dropdown.html.haml
new file mode 100644
index 00000000000..9b2f2fdcc93
--- /dev/null
+++ b/app/views/shared/_milestones_sort_dropdown.html.haml
@@ -0,0 +1,22 @@
+.dropdown.inline.prepend-left-10
+ %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown' } }
+ %span.light
+ - if @sort.present?
+ = milestone_sort_options_hash[@sort]
+ - else
+ = sort_title_due_date_soon
+ = icon('chevron-down')
+ %ul.dropdown-menu.dropdown-menu-align-right.dropdown-menu-sort
+ %li
+ = link_to page_filter_path(sort: sort_value_due_date_soon, label: true) do
+ = sort_title_due_date_soon
+ = link_to page_filter_path(sort: sort_value_due_date_later, label: true) do
+ = sort_title_due_date_later
+ = link_to page_filter_path(sort: sort_value_start_date_soon, label: true) do
+ = sort_title_start_date_soon
+ = link_to page_filter_path(sort: sort_value_start_date_later, label: true) do
+ = sort_title_start_date_later
+ = link_to page_filter_path(sort: sort_value_name, label: true) do
+ = sort_title_name_asc
+ = link_to page_filter_path(sort: sort_value_name_desc, label: true) do
+ = sort_title_name_desc
diff --git a/app/views/shared/empty_states/icons/_pipelines_empty.svg b/app/views/shared/empty_states/icons/_pipelines_empty.svg
new file mode 100644
index 00000000000..8119d5bebe0
--- /dev/null
+++ b/app/views/shared/empty_states/icons/_pipelines_empty.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 150"><g fill="none" fill-rule="evenodd" transform="translate(0-3)"><g transform="translate(0 105)"><g fill="#e5e5e5"><rect width="78" height="4" x="34" y="21" opacity=".5" rx="2"/><path d="m152 23c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2m14 0c0-1.105.887-2 1.998-2h4c1.104 0 1.998.888 1.998 2 0 1.105-.887 2-1.998 2h-4c-1.104 0-1.998-.888-1.998-2"/></g><g transform="translate(0 4)"><path fill="#98d7b2" fill-rule="nonzero" d="m19 38c-10.493 0-19-8.507-19-19 0-10.493 8.507-19 19-19 10.493 0 19 8.507 19 19 0 10.493-8.507 19-19 19m0-4c8.284 0 15-6.716 15-15 0-8.284-6.716-15-15-15-8.284 0-15 6.716-15 15 0 8.284 6.716 15 15 15"/><path fill="#31af64" d="m17.07 21.02l-2.829-2.829c-.786-.786-2.047-.781-2.828 0-.786.786-.781 2.047 0 2.828l4.243 4.243c.392.392.902.587 1.412.588.512.002 1.021-.193 1.41-.582l7.79-7.79c.777-.777.775-2.042-.006-2.823-.786-.786-2.045-.784-2.823-.006l-6.37 6.37"/></g><g fill="#e52c5a" transform="translate(102)"><path fill-rule="nonzero" d="m24 47.5c-12.979 0-23.5-10.521-23.5-23.5 0-12.979 10.521-23.5 23.5-23.5 12.979 0 23.5 10.521 23.5 23.5 0 12.979-10.521 23.5-23.5 23.5m0-5c10.217 0 18.5-8.283 18.5-18.5 0-10.217-8.283-18.5-18.5-18.5-10.217 0-18.5 8.283-18.5 18.5 0 10.217 8.283 18.5 18.5 18.5"/><path d="m28.24 24l2.833-2.833c1.167-1.167 1.167-3.067-.004-4.239-1.169-1.169-3.069-1.173-4.239-.004l-2.833 2.833-2.833-2.833c-1.167-1.167-3.067-1.167-4.239.004-1.169 1.169-1.173 3.069-.004 4.239l2.833 2.833-2.833 2.833c-1.167 1.167-1.167 3.067.004 4.239 1.169 1.169 3.069 1.173 4.239.004l2.833-2.833 2.833 2.833c1.167 1.167 3.067 1.167 4.239-.004 1.169-1.169 1.173-3.069.004-4.239l-2.833-2.833"/></g><path fill="#e5e5e5" fill-rule="nonzero" d="m236 37c-7.732 0-14-6.268-14-14 0-7.732 6.268-14 14-14 7.732 0 14 6.268 14 14 0 7.732-6.268 14-14 14m0-4c5.523 0 10-4.477 10-10 0-5.523-4.477-10-10-10-5.523 0-10 4.477-10 10 0 5.523 4.477 10 10 10"/></g><g transform="translate(69 3)"><path fill="#e5e5e5" fill-rule="nonzero" d="m4 11.99v60.02c0 4.413 3.583 7.99 8 7.99h89.991c4.419 0 8-3.579 8-7.99v-60.02c0-4.413-3.583-7.99-8-7.99h-89.991c-4.419 0-8 3.579-8 7.99m-4 0c0-6.622 5.378-11.99 12-11.99h89.991c6.629 0 12 5.367 12 11.99v60.02c0 6.622-5.378 11.99-12 11.99h-89.991c-6.629 0-12-5.367-12-11.99v-60.02m52.874 80.3l-13.253-15.292h34.76l-13.253 15.292c-2.237 2.582-6.01 2.585-8.253 0m3.02-2.62c.644.743 1.564.743 2.207 0l7.516-8.673h-17.24l7.516 8.673"/><rect width="18" height="6" x="15" y="23" fill="#fc8a51" rx="3"/><rect width="18" height="6" x="39" y="39" fill="#e52c5a" rx="3"/><rect width="18" height="6" x="33" y="55" fill="#e5e5e5" rx="3"/><rect width="12" height="6" x="39" y="23" fill="#fde5d8" rx="3"/><rect width="12" height="6" x="57" y="55" fill="#e52c5a" rx="3"/><rect width="12" height="6" x="15" y="55" fill="#b5a7dd" rx="3"/><rect width="18" height="6" x="81" y="23" fill="#fc8a51" rx="3"/><rect width="18" height="6" x="15" y="39" fill="#fde5d8" rx="3"/><rect width="6" height="6" x="57" y="23" fill="#e52c5a" rx="3"/><g fill="#fde5d8"><rect width="6" height="6" x="69" y="23" rx="3"/><rect width="6" height="6" x="75" y="39" rx="3"/></g><rect width="6" height="6" x="63" y="39" fill="#e52c5a" rx="3"/></g><g transform="matrix(.70711-.70711.70711.70711 84.34 52.5)"><path fill="#6b4fbb" fill-rule="nonzero" d="m28.02 67.48c-15.927-2.825-28.02-16.738-28.02-33.476 0-18.778 15.222-34 34-34 18.778 0 34 15.222 34 34 0 16.738-12.1 30.652-28.02 33.476.015.173.023.347.023.524v21.999c0 3.314-2.693 6-6 6-3.314 0-6-2.682-6-6v-21.999c0-.177.008-.351.023-.524m5.977-7.476c14.359 0 26-11.641 26-26 0-14.359-11.641-26-26-26-14.359 0-26 11.641-26 26 0 14.359 11.641 26 26 26"/><path fill="#fff" fill-opacity=".3" stroke="#6b4fbb" stroke-width="8" d="m31 71c16.569 0 30-13.431 30-30 0-16.569-13.431-30-30-30" transform="matrix(.86603.5-.5.86603 26.663-17.507)"/></g></g></svg> \ No newline at end of file
diff --git a/app/views/shared/empty_states/icons/_pipelines_failed.svg b/app/views/shared/empty_states/icons/_pipelines_failed.svg
new file mode 100644
index 00000000000..7dbabf7e4ef
--- /dev/null
+++ b/app/views/shared/empty_states/icons/_pipelines_failed.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 446 249" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path id="0" d="m260.03 114h23.972v-.013c19.972-.53 36-16.887 36-36.987 0-20.435-16.565-37-37-37-.993 0-1.977.039-2.95.116-4.95-14.605-18.773-25.12-35.05-25.12-5.464 0-10.652 1.185-15.32 3.311-6.649-9.841-17.909-16.311-30.68-16.311-20.435 0-37 16.565-37 37 0 .701.019 1.397.058 2.088-16.11 3.999-28.06 18.561-28.06 35.912 0 20.435 16.565 37 37 37 .324 0 .646-.004.968-.012"/><ellipse id="2" cx="41" cy="41" rx="41" ry="41"/><mask id="1" width="186" height="112" x="0" y="0" fill="#fff"><use xlink:href="#0"/></mask><mask id="3" width="82" height="82" x="0" y="0" fill="#fff"><use xlink:href="#2"/></mask></defs><g fill="none" fill-rule="evenodd"><g transform="matrix(.86603.5-.5.86603 228.11 137.43)"><path stroke="#b5a7dd" stroke-width="4" d="m.445.161c15.89 10.636 34.998 16.839 55.55 16.839"/><g transform="translate(56 4)"><path fill="#fb722e" d="m16 8c0-1.105.902-2 2.01-2h7.983c1.109 0 2.01.888 2.01 2 0 1.105-.902 2-2.01 2h-7.983c-1.109 0-2.01-.888-2.01-2m0 10c0-1.105.902-2 2.01-2h7.983c1.109 0 2.01.888 2.01 2 0 1.105-.902 2-2.01 2h-7.983c-1.109 0-2.01-.888-2.01-2"/><path fill="#fde5d8" fill-rule="nonzero" d="m4 22h6c3.315 0 6-2.685 6-5.997v-6.01c0-3.315-2.684-5.997-6-5.997h-6v18m-4-18.992c0-1.661 1.343-3.01 2.994-3.01h7.01c5.523 0 10 4.47 10 9.997v6.01c0 5.521-4.476 9.997-10 9.997h-7.01c-1.654 0-2.994-1.343-2.994-3.01v-19.984"/></g></g><g fill-rule="nonzero" transform="translate(257)"><path fill="#e5e5e5" d="m3.597 18.747c5.611-9.09 15.519-14.747 26.403-14.747 17.12 0 31 13.879 31 31 0 7.02-2.34 13.685-6.58 19.1l3.149 2.466c4.786-6.111 7.431-13.639 7.431-21.565 0-19.33-15.67-35-35-35-12.286 0-23.476 6.384-29.808 16.647l3.404 2.1"/><g transform="matrix(.96593.25882-.25882.96593 15.98 9.578)"><path fill="#b5a7dd" d="m12.426 11.592l-2.142 1.768-3.664-2.116c-.186-.107-.43-.042-.543.154l-1.229 2.129c-.116.2-.052.438.138.547l3.658 2.112-.455 2.735c-.109.657-.165 1.327-.165 2.01 0 .678.055 1.348.165 2.01l.455 2.735-3.658 2.112c-.186.107-.251.351-.138.547l1.229 2.129c.116.2.353.264.543.154l3.664-2.116 2.142 1.768c1.036.855 2.205 1.533 3.462 2l2.6.972v4.225c0 .215.179.393.405.393h2.458c.231 0 .405-.174.405-.393v-4.225l2.6-.972c1.257-.47 2.426-1.147 3.462-2l2.142-1.768 3.664 2.116c.186.107.43.042.543-.154l1.229-2.129c.116-.2.052-.438-.138-.547l-3.658-2.112.455-2.735c.109-.657.165-1.327.165-2.01 0-.678-.055-1.348-.165-2.01l-.455-2.735 3.658-2.112c.186-.107.251-.351.138-.547l-1.229-2.129c-.116-.2-.353-.264-.543-.154l-3.664 2.116-2.142-1.768c-1.036-.855-2.205-1.533-3.462-2l-2.6-.972v-4.225c0-.215-.179-.393-.405-.393h-2.458c-.231 0-.405.174-.405.393v4.225l-2.6.972c-1.257.47-2.426 1.147-3.462 2m2.062-5.749v-1.45c0-2.426 1.963-4.393 4.405-4.393h2.458c2.433 0 4.405 1.967 4.405 4.393v1.45c1.689.631 3.243 1.538 4.608 2.665l1.259-.727c2.101-1.213 4.786-.497 6.01 1.618l1.229 2.129c1.216 2.107.499 4.798-1.602 6.01l-1.257.726c.144.866.219 1.755.219 2.662 0 .907-.075 1.796-.219 2.662l1.257.726c2.101 1.213 2.823 3.896 1.602 6.01l-1.229 2.129c-1.216 2.107-3.906 2.832-6.01 1.618l-1.259-.727c-1.365 1.127-2.92 2.034-4.608 2.665v1.45c0 2.426-1.963 4.393-4.405 4.393h-2.458c-2.433 0-4.405-1.967-4.405-4.393v-1.45c-1.689-.631-3.243-1.538-4.608-2.665l-1.259.727c-2.101 1.213-4.786.497-6.01-1.618l-1.229-2.129c-1.216-2.107-.499-4.798 1.602-6.01l1.257-.726c-.144-.866-.219-1.755-.219-2.662 0-.907.075-1.796.219-2.662l-1.257-.726c-2.101-1.213-2.823-3.896-1.602-6.01l1.229-2.129c1.216-2.107 3.906-2.832 6.01-1.618l1.259.727c1.365-1.127 2.92-2.034 4.608-2.665"/><path fill="#6b4fbb" d="m20.12 23.366c1.347 0 2.439-1.092 2.439-2.439 0-1.347-1.092-2.439-2.439-2.439-1.347 0-2.439 1.092-2.439 2.439 0 1.347 1.092 2.439 2.439 2.439m0 4c-3.556 0-6.439-2.883-6.439-6.439 0-3.556 2.883-6.439 6.439-6.439 3.556 0 6.439 2.883 6.439 6.439 0 3.556-2.883 6.439-6.439 6.439"/></g></g><use fill="#fff" stroke="#e5e5e5" stroke-width="8" mask="url(#1)" stroke-linejoin="round" xlink:href="#0"/><g transform="translate(175 58)"><use fill="#fff" stroke="#e5e5e5" stroke-width="8" mask="url(#3)" xlink:href="#2"/><g fill-rule="nonzero"><path fill="#e5e5e5" d="m41 78c20.435 0 37-16.565 37-37 0-20.435-16.565-37-37-37-20.435 0-37 16.565-37 37 0 20.435 16.565 37 37 37m0 4c-22.644 0-41-18.356-41-41 0-22.644 18.356-41 41-41 22.644 0 41 18.356 41 41 0 22.644-18.356 41-41 41"/><g transform="matrix(.96593.25882-.25882.96593 23.581 9.415)"><path fill="#b5a7dd" d="m14.821 13.655l-2.142 1.768-3.933-2.271c-.72-.416-1.634-.171-2.046.543l-1.507 2.61c-.409.708-.161 1.631.553 2.043l3.926 2.267-.455 2.735c-.145.869-.218 1.754-.218 2.65 0 .896.073 1.782.218 2.65l.455 2.735-3.926 2.267c-.72.416-.965 1.329-.553 2.043l1.507 2.61c.409.708 1.332.955 2.046.543l3.933-2.271 2.142 1.768c1.369 1.131 2.916 2.027 4.579 2.648l2.6.972v4.534c0 .831.669 1.5 1.493 1.5h3.01c.817 0 1.493-.676 1.493-1.5v-4.534l2.6-.972c1.663-.621 3.21-1.518 4.579-2.648l2.142-1.768 3.933 2.271c.72.416 1.634.171 2.046-.543l1.507-2.61c.409-.708.161-1.631-.553-2.043l-3.926-2.267.455-2.735c.145-.869.218-1.754.218-2.65 0-.896-.073-1.782-.218-2.65l-.455-2.735 3.926-2.267c.72-.416.965-1.329.553-2.043l-1.507-2.61c-.409-.708-1.332-.955-2.046-.543l-3.933 2.271-2.142-1.768c-1.369-1.131-2.916-2.027-4.579-2.648l-2.6-.972v-4.534c0-.831-.669-1.5-1.493-1.5h-3.01c-.817 0-1.493.676-1.493 1.5v4.534l-2.6.972c-1.663.621-3.21 1.518-4.579 2.648m3.179-6.395v-1.759c0-3.038 2.471-5.5 5.493-5.5h3.01c3.034 0 5.493 2.46 5.493 5.5v1.759c2.098.784 4.03 1.91 5.725 3.311l1.528-.882c2.631-1.519 5.999-.61 7.51 2.01l1.507 2.61c1.517 2.627.616 5.987-2.02 7.507l-1.525.881c.179 1.076.272 2.18.272 3.307 0 1.127-.093 2.231-.272 3.307l1.525.881c2.631 1.519 3.528 4.89 2.02 7.507l-1.507 2.61c-1.517 2.627-4.877 3.527-7.51 2.01l-1.528-.882c-1.696 1.401-3.627 2.527-5.725 3.311v1.759c0 3.038-2.471 5.5-5.493 5.5h-3.01c-3.034 0-5.493-2.46-5.493-5.5v-1.759c-2.098-.784-4.03-1.91-5.725-3.311l-1.528.882c-2.631 1.519-5.999.61-7.51-2.01l-1.507-2.61c-1.517-2.627-.616-5.987 2.02-7.507l1.525-.881c-.179-1.076-.272-2.18-.272-3.307 0-1.127.093-2.231.272-3.307l-1.525-.881c-2.631-1.519-3.528-4.89-2.02-7.507l1.507-2.61c1.517-2.627 4.877-3.527 7.51-2.01l1.528.882c1.696-1.401 3.627-2.527 5.725-3.311"/><path fill="#6b4fbb" d="m25 30c2.209 0 4-1.791 4-4 0-2.209-1.791-4-4-4-2.209 0-4 1.791-4 4 0 2.209 1.791 4 4 4m0 4c-4.418 0-8-3.582-8-8 0-4.418 3.582-8 8-8 4.418 0 8 3.582 8 8 0 4.418-3.582 8-8 8"/></g></g></g><g transform="translate(140 161)"><path fill="#e5e5e5" fill-rule="nonzero" d="m4 8.541v30.01c0 2.202 1.793 3.995 4 3.995h20c2.209 0 4-1.789 4-3.995v-30.01c0-2.202-1.793-3.995-4-3.995h-20c-2.209 0-4 1.789-4 3.995m-4 0c0-4.416 3.583-7.995 8-7.995h20c4.416 0 8 3.584 8 7.995v30.01c0 4.416-3.583 7.995-8 7.995h-20c-4.416 0-8-3.584-8-7.995v-30.01"/><g fill="#fb722e"><rect width="4" height="11" x="10" y="18.545" rx="2"/><rect width="4" height="11" x="21" y="18.545" rx="2"/></g></g><path fill="#e5e5e5" fill-rule="nonzero" d="m445.16 245.34c-16.874-11.778-110.62-20.336-222.14-20.336-111.61 0-205.4 8.571-222.18 20.364-.904.635-1.121 1.883-.486 2.786.635.904 1.883 1.121 2.786.486 15.756-11.07 109.46-19.636 219.88-19.636 110.34 0 203.99 8.55 219.85 19.617.906.632 2.153.41 2.785-.495.632-.906.41-2.153-.495-2.785"/></g></svg> \ No newline at end of file
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index a95020a9be8..09f946f1d88 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -17,7 +17,7 @@
.stats
%span
= icon('bookmark')
- = number_with_delimiter(group.projects.count)
+ = number_with_delimiter(group.projects.non_archived.count)
%span
= icon('users')
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 25a4aec0a38..884bd3ca9ca 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -3,7 +3,7 @@
= page_specific_javascript_bundle_tag('common_vue')
= page_specific_javascript_bundle_tag('issuable')
-%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "101", "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
+%aside.right-sidebar.js-right-sidebar{ data: { "offset-top" => "102", "spy" => "affix" }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite' }
.issuable-sidebar
- can_edit_issuable = can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
.block.issuable-sidebar-header
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index c57282c5742..c0699b13719 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -10,7 +10,7 @@
.js-projects-list-holder
- if projects.any?
- %ul.projects-list.content-list
+ %ul.projects-list
- projects.each_with_index do |project, i|
- css_class = (i >= projects_limit) ? 'hide' : nil
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index df21857e1ad..059aeebaf34 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -10,44 +10,44 @@
%li.project-row{ class: css_class }
= cache(cache_key) do
+ - if avatar
+ .avatar-container.s40
+ - if use_creator_avatar
+ = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
+ - else
+ = project_icon(project, alt: '', class: 'avatar project-avatar s40')
+ .project-details
+ %h3.prepend-top-0.append-bottom-0
+ = link_to project_path(project), class: dom_class(project) do
+ %span.project-full-name
+ %span.namespace-name
+ - if project.namespace && !skip_namespace
+ = project.namespace.human_name
+ \/
+ %span.project-name.filter-title
+ = project.name
+
+ - if show_last_commit_as_description
+ .description.prepend-top-5
+ = link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
+ class: "commit-row-message"
+ - elsif project.description.present?
+ .description.prepend-top-5
+ = markdown_field(project, :description)
+
.controls
- if project.archived
- %span.label.label-warning archived
+ %span.prepend-left-10.label.label-warning archived
- if project.pipeline_status.has_status?
- %span
+ %span.prepend-left-10
= render_project_pipeline_status(project.pipeline_status)
- if forks
- %span
+ %span.prepend-left-10
= icon('code-fork')
= number_with_delimiter(project.forks_count)
- if stars
- %span
+ %span.prepend-left-10
= icon('star')
= number_with_delimiter(project.star_count)
- %span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) }
+ %span.prepend-left-10.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(project) }
= visibility_level_icon(project.visibility_level, fw: true)
-
- .title
- = link_to project_path(project), class: dom_class(project) do
- - if avatar
- .dash-project-avatar
- .avatar-container.s40
- - if use_creator_avatar
- = image_tag avatar_icon(project.creator.email, 40), class: "avatar s40", alt:''
- - else
- = project_icon(project, alt: '', class: 'avatar project-avatar s40')
- %span.project-full-name
- %span.namespace-name
- - if project.namespace && !skip_namespace
- = project.namespace.human_name
- \/
- %span.project-name.filter-title
- = project.name
-
- - if show_last_commit_as_description
- .description
- = link_to_gfm project.commit.title, namespace_project_commit_path(project.namespace, project, project.commit),
- class: "commit-row-message"
- - elsif project.description.present?
- .description
- = markdown_field(project, :description)
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index dc9a3b0d0df..601187455b3 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -13,7 +13,7 @@
.cover-block.user-cover-block
.cover-controls
- if @user == current_user
- = link_to profile_path, class: 'btn btn-gray' do
+ = link_to profile_path, class: 'btn btn-gray has-tooltip', title: 'Edit profile', 'aria-label': 'Edit profile' do
= icon('pencil')
- elsif current_user
- if @user.abuse_report
@@ -24,7 +24,7 @@
= link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('exclamation-circle')
- = link_to user_path(@user, rss_url_options), class: 'btn btn-gray' do
+ = link_to user_path(@user, rss_url_options), class: 'btn btn-gray has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do
= icon('rss')
- if current_user && current_user.admin?
= link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
@@ -44,7 +44,7 @@
%span.middle-dot-divider
@#{@user.username}
%span.middle-dot-divider
- Member since #{@user.created_at.to_s(:medium)}
+ Member since #{@user.created_at.to_date.to_s(:long)}
.cover-desc
- unless @user.public_email.blank?
@@ -97,7 +97,8 @@
Snippets
%div{ class: container_class }
- .user-callout{ 'callout-svg' => custom_icon('icon_customization') }
+ - if @user == current_user
+ .user-callout{ 'callout-svg' => custom_icon('icon_customization') }
.tab-content
#activity.tab-pane
.row-content-block.calender-block.white.second-block.hidden-xs
diff --git a/bin/with_env b/bin/with_env
new file mode 100755
index 00000000000..e678fa2f0cc
--- /dev/null
+++ b/bin/with_env
@@ -0,0 +1,16 @@
+#!/bin/sh
+# Usage: with_env ENV_FILE COMMAND [ARGS...]
+#
+# This script lets you modify the environment of an executable before
+# launching it. It uses an 'env file' which must contain lines like
+# 'MY_VARIABLE="my value"'.
+#
+env_file=$1
+shift
+
+# Use set -a to export all variables defined in env_file.
+set -a
+. "${env_file}"
+set +a
+
+exec "$@"
diff --git a/changelogs/unreleased/1051-api-create-users-without-password.yml b/changelogs/unreleased/1051-api-create-users-without-password.yml
deleted file mode 100644
index 24b5a73b45c..00000000000
--- a/changelogs/unreleased/1051-api-create-users-without-password.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Optionally make users created via the API set their password
-merge_request: 8957
-author: Joost Rijneveld
diff --git a/changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml b/changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml
deleted file mode 100644
index 4a1a199673c..00000000000
--- a/changelogs/unreleased/12726-preserve-issues-after-deleting-users.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Deleting a user doesn't delete issues they've created/are assigned to
-merge_request: 7393
-author:
diff --git a/changelogs/unreleased/1363-redo-mailroom-support.yml b/changelogs/unreleased/1363-redo-mailroom-support.yml
deleted file mode 100644
index 8ed206f4fdb..00000000000
--- a/changelogs/unreleased/1363-redo-mailroom-support.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Redo internals of Incoming Mail Support
-merge_request: 9385
-author:
diff --git a/changelogs/unreleased/1381-present-commits-pagination-headers-correctly.yml b/changelogs/unreleased/1381-present-commits-pagination-headers-correctly.yml
deleted file mode 100644
index 1b7e294bd67..00000000000
--- a/changelogs/unreleased/1381-present-commits-pagination-headers-correctly.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "GET 'projects/:id/repository/commits' endpoint improvements"
-merge_request: 9679
-author: George Andrinopoulos, Jordan Ryan Reuter
diff --git a/changelogs/unreleased/14492-change-fork-endpoint.yml b/changelogs/unreleased/14492-change-fork-endpoint.yml
deleted file mode 100644
index 39024b51b54..00000000000
--- a/changelogs/unreleased/14492-change-fork-endpoint.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Move /projects/fork/:id to /projects/:id/fork
-merge_request: 8940
-author:
diff --git a/changelogs/unreleased/14748-runner-version-in-admin-views.yml b/changelogs/unreleased/14748-runner-version-in-admin-views.yml
deleted file mode 100644
index 2478a81c824..00000000000
--- a/changelogs/unreleased/14748-runner-version-in-admin-views.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add runner version to /admin/runners view
-merge_request: 8733
-author: Jonathon Reinhart
diff --git a/changelogs/unreleased/1648-remove-remnants-of-git-annex-from-ce.yml b/changelogs/unreleased/1648-remove-remnants-of-git-annex-from-ce.yml
deleted file mode 100644
index f247fe35439..00000000000
--- a/changelogs/unreleased/1648-remove-remnants-of-git-annex-from-ce.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove remnants of git annex support.
-merge_request:
-author:
diff --git a/changelogs/unreleased/18962-update-issues-button-jumps.yml b/changelogs/unreleased/18962-update-issues-button-jumps.yml
deleted file mode 100644
index 7be136ac4ff..00000000000
--- a/changelogs/unreleased/18962-update-issues-button-jumps.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Align bulk update issues button to the right
-merge_request:
-author:
diff --git a/changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml b/changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml
deleted file mode 100644
index d74057dca8a..00000000000
--- a/changelogs/unreleased/19302-wiki-page-delete-does-not-trigger-the-webhook.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Execute web hooks for WikiPage delete operation
-merge_request: 8198
-author:
diff --git a/changelogs/unreleased/1937-https-clone-url-username.yml b/changelogs/unreleased/1937-https-clone-url-username.yml
deleted file mode 100644
index fa89d94e0f3..00000000000
--- a/changelogs/unreleased/1937-https-clone-url-username.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add the Username to the HTTP(S) clone URL of a Repository
-merge_request: 9347
-author: Jan Christophersen
diff --git a/changelogs/unreleased/19497-hide-relevant-info-when-project-issues-are-disabled.yml b/changelogs/unreleased/19497-hide-relevant-info-when-project-issues-are-disabled.yml
deleted file mode 100644
index eceb2b9fac6..00000000000
--- a/changelogs/unreleased/19497-hide-relevant-info-when-project-issues-are-disabled.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Hide issue info when project issues are disabled
-merge_request:
-author: George Andrinopoulos
diff --git a/changelogs/unreleased/20495-plus-icon-button.yml b/changelogs/unreleased/20495-plus-icon-button.yml
deleted file mode 100644
index 0f8650eb7b6..00000000000
--- a/changelogs/unreleased/20495-plus-icon-button.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove plus icon from MR button on compare view
-merge_request:
-author:
diff --git a/changelogs/unreleased/20732_member_exists_409.yml b/changelogs/unreleased/20732_member_exists_409.yml
deleted file mode 100644
index 135647c7ac3..00000000000
--- a/changelogs/unreleased/20732_member_exists_409.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'Add member: Always return 409 when a member exists'
-merge_request:
-author:
diff --git a/changelogs/unreleased/21240_snippets_line_ending.yml b/changelogs/unreleased/21240_snippets_line_ending.yml
deleted file mode 100644
index 880fdd2c9ed..00000000000
--- a/changelogs/unreleased/21240_snippets_line_ending.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Download snippets with LF line-endings by default
-merge_request: 8999
-author:
diff --git a/changelogs/unreleased/21605-allow-html5-details.yml b/changelogs/unreleased/21605-allow-html5-details.yml
deleted file mode 100644
index b0c654783d9..00000000000
--- a/changelogs/unreleased/21605-allow-html5-details.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: SanitizationFilter allows html5 details and summary tags
-merge_request: 6568
-author:
diff --git a/changelogs/unreleased/22018-api-milestone-merge-requests.yml b/changelogs/unreleased/22018-api-milestone-merge-requests.yml
deleted file mode 100644
index ccad2ec838c..00000000000
--- a/changelogs/unreleased/22018-api-milestone-merge-requests.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Adds API endpoint to fetch all merge request for a single milestone
-merge_request:
-author: Joren De Groof
diff --git a/changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml b/changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml
deleted file mode 100644
index 028923b83cf..00000000000
--- a/changelogs/unreleased/22132-rename-branch-name-params-to-branch.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Standardize branch name params as branch on V4 API
-merge_request: 8936
-author:
diff --git a/changelogs/unreleased/22466-task-list-alignment.yml b/changelogs/unreleased/22466-task-list-alignment.yml
deleted file mode 100644
index 6e6ccb873ec..00000000000
--- a/changelogs/unreleased/22466-task-list-alignment.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Align task list checkboxes
-merge_request: 6487
-author: Jared Deckard <jared.deckard@gmail.com>
diff --git a/changelogs/unreleased/22562-todos-filters.yml b/changelogs/unreleased/22562-todos-filters.yml
deleted file mode 100644
index 9cca138744a..00000000000
--- a/changelogs/unreleased/22562-todos-filters.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix Sort dropdown reflow issue
-merge_request: 9533
-author: Jarkko Tuunanen
diff --git a/changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml b/changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml
deleted file mode 100644
index 9b3c2bd9278..00000000000
--- a/changelogs/unreleased/22645-add-discussion-contribs-to-calendar.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add discussion events to contributions calendar
-merge_request: 8821
-author:
diff --git a/changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml b/changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml
deleted file mode 100644
index 05d5993ddf3..00000000000
--- a/changelogs/unreleased/22818-licence-gitignore-and-yml-endpoints-removal.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: V3 deprecated templates endpoints removal
-merge_request: 8853
-author:
diff --git a/changelogs/unreleased/22850-404-when-requesting-build-trace.yml b/changelogs/unreleased/22850-404-when-requesting-build-trace.yml
new file mode 100644
index 00000000000..6b442130d9b
--- /dev/null
+++ b/changelogs/unreleased/22850-404-when-requesting-build-trace.yml
@@ -0,0 +1,4 @@
+---
+title: Resolve "404 when requesting build trace"
+merge_request: 9759
+author: dosuken123
diff --git a/changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml b/changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml
deleted file mode 100644
index a53e7d77c16..00000000000
--- a/changelogs/unreleased/22951-fix-todos-api-endpoint-error-for-commits.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add spec for todo with target_type Commit
-merge_request: 9351
-author: George Andrinopoulos
diff --git a/changelogs/unreleased/23061-consolidate-project-lists.yml b/changelogs/unreleased/23061-consolidate-project-lists.yml
deleted file mode 100644
index dbb8fed55c0..00000000000
--- a/changelogs/unreleased/23061-consolidate-project-lists.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Consolidate /projects endpoint'
-merge_request: 8962
-author:
diff --git a/changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml b/changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml
deleted file mode 100644
index f7c856040e0..00000000000
--- a/changelogs/unreleased/23062-allow-git-log-to-accept-follow-and-skip.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make Git history follow renames again by performing the --skip in Ruby
-merge_request:
-author:
diff --git a/changelogs/unreleased/23104-remove-public-param-for-projects.yml b/changelogs/unreleased/23104-remove-public-param-for-projects.yml
deleted file mode 100644
index 78eb785279f..00000000000
--- a/changelogs/unreleased/23104-remove-public-param-for-projects.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: remove `public` param for projects'
-merge_request: 8736
-author:
diff --git a/changelogs/unreleased/23535-folders-in-wiki-repository.yml b/changelogs/unreleased/23535-folders-in-wiki-repository.yml
deleted file mode 100644
index 05212b608d4..00000000000
--- a/changelogs/unreleased/23535-folders-in-wiki-repository.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Show directory hierarchy when listing wiki pages
-merge_request: 8133
-author: Alex Braha Stoll
diff --git a/changelogs/unreleased/23819-fix-milestone-counters-to-top-right-of-panel-headings.yml b/changelogs/unreleased/23819-fix-milestone-counters-to-top-right-of-panel-headings.yml
deleted file mode 100644
index 628db8a5419..00000000000
--- a/changelogs/unreleased/23819-fix-milestone-counters-to-top-right-of-panel-headings.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix position of counter in milestone panels
-merge_request: 7842
-author: Andrew Smith (EspadaV8)
diff --git a/changelogs/unreleased/23862-fix-group-project-count.yml b/changelogs/unreleased/23862-fix-group-project-count.yml
new file mode 100644
index 00000000000..7b2e9f9bfa6
--- /dev/null
+++ b/changelogs/unreleased/23862-fix-group-project-count.yml
@@ -0,0 +1,4 @@
+---
+title: Adding non_archived scope for counting projects
+merge_request: 8305
+author: Naveen Kumar
diff --git a/changelogs/unreleased/23948-assign-to-me.yml b/changelogs/unreleased/23948-assign-to-me.yml
deleted file mode 100644
index d73aa92b0e9..00000000000
--- a/changelogs/unreleased/23948-assign-to-me.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Re-add Assign to me link to Merge Request and Issues
-merge_request:
-author:
diff --git a/changelogs/unreleased/23993-drop-ci_projects.yml b/changelogs/unreleased/23993-drop-ci_projects.yml
deleted file mode 100644
index ee9cf774e37..00000000000
--- a/changelogs/unreleased/23993-drop-ci_projects.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Drop unused ci_projects table and some unused project_id columns,
- then rename gl_project_id to project_id. Stop exporting job trace when
- exporting projects.
-merge_request: 9378
-author: David Wagner
diff --git a/changelogs/unreleased/24215-closed-issues-board.yml b/changelogs/unreleased/24215-closed-issues-board.yml
new file mode 100644
index 00000000000..678ec34b274
--- /dev/null
+++ b/changelogs/unreleased/24215-closed-issues-board.yml
@@ -0,0 +1,4 @@
+---
+title: Display all closed issues in “done” board list
+merge_request:
+author:
diff --git a/changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml b/changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml
deleted file mode 100644
index fa137a29cb4..00000000000
--- a/changelogs/unreleased/24333-close-issues-with-merge-request-title-ui.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show Issues mentioned / being closed from a Merge Requests title below the
- 'Accept Merge Request' button
-merge_request: 9194
-author: Jan Christophersen
diff --git a/changelogs/unreleased/24683-sidebar-spinners.yml b/changelogs/unreleased/24683-sidebar-spinners.yml
deleted file mode 100644
index 3fec273152f..00000000000
--- a/changelogs/unreleased/24683-sidebar-spinners.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: hide loading spinners for server-rendered sidebar fields
-merge_request:
-author:
diff --git a/changelogs/unreleased/24976-start-of-line-mention.yml b/changelogs/unreleased/24976-start-of-line-mention.yml
deleted file mode 100644
index 99208aac87c..00000000000
--- a/changelogs/unreleased/24976-start-of-line-mention.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added a feature to create a 'directly addressed' Todo when mentioned in the beginning of a line.
-merge_request: 7926
-author: Ershad Kunnakkadan
diff --git a/changelogs/unreleased/24998-fix-typo-gitlab-config-file.yml b/changelogs/unreleased/24998-fix-typo-gitlab-config-file.yml
deleted file mode 100644
index 3b90466e3af..00000000000
--- a/changelogs/unreleased/24998-fix-typo-gitlab-config-file.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix typo in Gitlab config file
-merge_request: 9702
-author: medied
diff --git a/changelogs/unreleased/25188-polyfill-es-symbol.yml b/changelogs/unreleased/25188-polyfill-es-symbol.yml
new file mode 100644
index 00000000000..d0cf36b9ec6
--- /dev/null
+++ b/changelogs/unreleased/25188-polyfill-es-symbol.yml
@@ -0,0 +1,4 @@
+---
+title: Add ECMAScript polyfills for Symbol and Array.find
+merge_request: 10120
+author:
diff --git a/changelogs/unreleased/25367-add-impersonation-token.yml b/changelogs/unreleased/25367-add-impersonation-token.yml
deleted file mode 100644
index 4a30f960036..00000000000
--- a/changelogs/unreleased/25367-add-impersonation-token.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Manage user personal access tokens through api and add impersonation tokens
-merge_request: 9099
-author: Simon Vocella
diff --git a/changelogs/unreleased/25437-just-emoji.yml b/changelogs/unreleased/25437-just-emoji.yml
deleted file mode 100644
index ceb81a47f2d..00000000000
--- a/changelogs/unreleased/25437-just-emoji.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Introduce /award slash command; Allow posting of just an emoji in comment
-merge_request: 9382
-author: mhasbini
diff --git a/changelogs/unreleased/25465-todo-done-clicking-is-kind-of-unsafe.yml b/changelogs/unreleased/25465-todo-done-clicking-is-kind-of-unsafe.yml
deleted file mode 100644
index e9d46f6b122..00000000000
--- a/changelogs/unreleased/25465-todo-done-clicking-is-kind-of-unsafe.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Todo done clicking is kind of unusable
-merge_request: 8691
-author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/25503_issues_finder_performance.yml b/changelogs/unreleased/25503_issues_finder_performance.yml
deleted file mode 100644
index 87964269c6d..00000000000
--- a/changelogs/unreleased/25503_issues_finder_performance.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Filter by projects in the end of search
-merge_request: 9030
-author:
diff --git a/changelogs/unreleased/25709-diff-file-overflow.yml b/changelogs/unreleased/25709-diff-file-overflow.yml
deleted file mode 100644
index 7d1b2b36ab8..00000000000
--- a/changelogs/unreleased/25709-diff-file-overflow.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Responsive title in diffs inline, side by side, with and without sidebar
-merge_request: 8475
-author:
diff --git a/changelogs/unreleased/25920-create-issue-from-failing-build.yml b/changelogs/unreleased/25920-create-issue-from-failing-build.yml
deleted file mode 100644
index 580d1074aa7..00000000000
--- a/changelogs/unreleased/25920-create-issue-from-failing-build.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add button to create issue for failing build
-merge_request: 9391
-author: Alex Sanford
diff --git a/changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml b/changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml
deleted file mode 100644
index 799c5277207..00000000000
--- a/changelogs/unreleased/26087-asciidoc-cicd-badges-snippet.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added AsciiDoc Snippet to CI/CD Badges
-merge_request: 9164
-author: Jan Christophersen
diff --git a/changelogs/unreleased/26136-list-repository-tree-api-doc.yml b/changelogs/unreleased/26136-list-repository-tree-api-doc.yml
deleted file mode 100644
index 85d8bc6ca8a..00000000000
--- a/changelogs/unreleased/26136-list-repository-tree-api-doc.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make documentation of list repository tree API call more detailed
-merge_request: 9532
-author: Marius Kleiner
diff --git a/changelogs/unreleased/26206-fix-download-dropdown.yml b/changelogs/unreleased/26206-fix-download-dropdown.yml
deleted file mode 100644
index a6c101375bb..00000000000
--- a/changelogs/unreleased/26206-fix-download-dropdown.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Set dropdown height fixed to 250px and make it scrollable
-merge_request: 9063
-author:
diff --git a/changelogs/unreleased/26286-most-recent-activity-profile-header.yml b/changelogs/unreleased/26286-most-recent-activity-profile-header.yml
deleted file mode 100644
index 74d5a43a804..00000000000
--- a/changelogs/unreleased/26286-most-recent-activity-profile-header.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added 'Most Recent Activity' header to the User Profile page
-merge_request: 9189
-author: Jan Christophersen
diff --git a/changelogs/unreleased/26287-link-branch-in-calendar-activity.yml b/changelogs/unreleased/26287-link-branch-in-calendar-activity.yml
deleted file mode 100644
index 35855578d21..00000000000
--- a/changelogs/unreleased/26287-link-branch-in-calendar-activity.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Links to Branches in Calendar Activity
-merge_request: 9224
-author: Jan Christophersen
diff --git a/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml b/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml
deleted file mode 100644
index 6ee8e5724bc..00000000000
--- a/changelogs/unreleased/2629-show-public-rss-feeds-to-anonymous-users.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Show public RSS feeds to anonymous users
-merge_request: 9596
-author: Michael Kozono
diff --git a/changelogs/unreleased/26315-unify-labels-filter-behavior.yml b/changelogs/unreleased/26315-unify-labels-filter-behavior.yml
deleted file mode 100644
index cd2f40c94fe..00000000000
--- a/changelogs/unreleased/26315-unify-labels-filter-behavior.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Unify issues search behavior by always filtering when ALL labels matches
-merge_request: 8849
-author:
diff --git a/changelogs/unreleased/26348-cleanup-navigation-order-groups.yml b/changelogs/unreleased/26348-cleanup-navigation-order-groups.yml
deleted file mode 100644
index ce888baa32f..00000000000
--- a/changelogs/unreleased/26348-cleanup-navigation-order-groups.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Clean-up Groups navigation order
-merge_request: 9309
-author:
diff --git a/changelogs/unreleased/26348-cleanup-navigation-order.yml b/changelogs/unreleased/26348-cleanup-navigation-order.yml
deleted file mode 100644
index d5324f9e025..00000000000
--- a/changelogs/unreleased/26348-cleanup-navigation-order.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Clean-up Project navigation order
-merge_request: 9272
-author:
diff --git a/changelogs/unreleased/26371-native-emojis-v3-code.yml b/changelogs/unreleased/26371-native-emojis-v3-code.yml
deleted file mode 100644
index 88346711490..00000000000
--- a/changelogs/unreleased/26371-native-emojis-v3-code.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use native unicode emojis
-merge_request:
-author:
diff --git a/changelogs/unreleased/26379-iid-param.yml b/changelogs/unreleased/26379-iid-param.yml
deleted file mode 100644
index ac743e68d6f..00000000000
--- a/changelogs/unreleased/26379-iid-param.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: add :iids param to IssuableFinder (resolve technical dept)
-merge_request: 9222
-author: mhasbini
diff --git a/changelogs/unreleased/26500-informative-slack-notifications.yml b/changelogs/unreleased/26500-informative-slack-notifications.yml
deleted file mode 100644
index 342235424f4..00000000000
--- a/changelogs/unreleased/26500-informative-slack-notifications.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add user & build links in Slack Notifications
-merge_request: 8641
-author: Poornima M
diff --git a/changelogs/unreleased/26651-cannot-move-project-into-group.yml b/changelogs/unreleased/26651-cannot-move-project-into-group.yml
deleted file mode 100644
index 244a19a627d..00000000000
--- a/changelogs/unreleased/26651-cannot-move-project-into-group.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Specify in the documentation that only projects owners can transfer projects
-merge_request:
-author:
diff --git a/changelogs/unreleased/26703-todos-count.yml b/changelogs/unreleased/26703-todos-count.yml
deleted file mode 100644
index 24fd0c406e2..00000000000
--- a/changelogs/unreleased/26703-todos-count.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: show 99+ for large count in todos notification bell
-merge_request: 9171
-author: mhasbini
diff --git a/changelogs/unreleased/26705-filter-todos-by-manual-add.yml b/changelogs/unreleased/26705-filter-todos-by-manual-add.yml
deleted file mode 100644
index 3521496a20e..00000000000
--- a/changelogs/unreleased/26705-filter-todos-by-manual-add.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Filter todos by manual add
-merge_request: 8691
-author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/26732-combine-deploy-keys-and-push-rules-and-mirror-repository-and-protect-branches-settings-pages.yml b/changelogs/unreleased/26732-combine-deploy-keys-and-push-rules-and-mirror-repository-and-protect-branches-settings-pages.yml
deleted file mode 100644
index 6fc4615dab8..00000000000
--- a/changelogs/unreleased/26732-combine-deploy-keys-and-push-rules-and-mirror-repository-and-protect-branches-settings-pages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Combined deploy keys, push rules, protect branches and mirror repository settings options into a single one called
- Repository
-merge_request:
-author:
diff --git a/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml b/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml
deleted file mode 100644
index 15da43b8091..00000000000
--- a/changelogs/unreleased/26744-add-omniauth-oauth2-generic-strategy.yml
+++ /dev/null
@@ -1,3 +0,0 @@
-title: Add the oauth2_generic OmniAuth strategy
-merge_request: 9048
-author: Joe Marty \ No newline at end of file
diff --git a/changelogs/unreleased/26790-label-color-todos.yml b/changelogs/unreleased/26790-label-color-todos.yml
deleted file mode 100644
index 74084473d81..00000000000
--- a/changelogs/unreleased/26790-label-color-todos.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: fix background color for labels mention in todo
-merge_request: 9155
-author: mhasbini
diff --git a/changelogs/unreleased/26847-api-pipelines-use-basic.yml b/changelogs/unreleased/26847-api-pipelines-use-basic.yml
deleted file mode 100644
index 2034a4ba080..00000000000
--- a/changelogs/unreleased/26847-api-pipelines-use-basic.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Expose pipelines as PipelineBasic `api/v3/projects/:id/pipelines`
-merge_request: 8875
-author:
diff --git a/changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml b/changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml
deleted file mode 100644
index 3d6400cba76..00000000000
--- a/changelogs/unreleased/26875-builds-api-endpoint-skipped-scope.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add all available statuses to scope filter for project builds endpoint
-merge_request:
-author: George Andrinopoulos
diff --git a/changelogs/unreleased/26900-pipelines-tabs.yml b/changelogs/unreleased/26900-pipelines-tabs.yml
deleted file mode 100644
index f08514c621f..00000000000
--- a/changelogs/unreleased/26900-pipelines-tabs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Adds Pending and Finished tabs to pipelines page
-merge_request:
-author:
diff --git a/changelogs/unreleased/26957-tanuki-anim-hang.yml b/changelogs/unreleased/26957-tanuki-anim-hang.yml
deleted file mode 100644
index c7b4b9ebdfd..00000000000
--- a/changelogs/unreleased/26957-tanuki-anim-hang.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: don't animate logo when downloading files
-merge_request:
-author:
diff --git a/changelogs/unreleased/27032-add-a-house-keeping-api-call.yml b/changelogs/unreleased/27032-add-a-house-keeping-api-call.yml
deleted file mode 100644
index a9f70e339c0..00000000000
--- a/changelogs/unreleased/27032-add-a-house-keeping-api-call.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add housekeeping endpoint for Projects API
-merge_request: 9421
-author:
diff --git a/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml b/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
deleted file mode 100644
index ee236310a71..00000000000
--- a/changelogs/unreleased/27142-api-replace-destroy-with-stop-environment.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: API: Add environment stop action
-merge_request: 8808
-author:
diff --git a/changelogs/unreleased/27287-label-dropdown-error-messages.yml b/changelogs/unreleased/27287-label-dropdown-error-messages.yml
deleted file mode 100644
index dfd4102c324..00000000000
--- a/changelogs/unreleased/27287-label-dropdown-error-messages.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix displaying error messages for create label dropdown
-merge_request: 9058
-author: Tom Koole
diff --git a/changelogs/unreleased/27336-add-environment-url-link-to-terminal-page.yml b/changelogs/unreleased/27336-add-environment-url-link-to-terminal-page.yml
deleted file mode 100644
index dd4907166c4..00000000000
--- a/changelogs/unreleased/27336-add-environment-url-link-to-terminal-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added external environment link to web terminal view
-merge_request: 8303
-author:
diff --git a/changelogs/unreleased/27354-navigation-new-button.yml b/changelogs/unreleased/27354-navigation-new-button.yml
deleted file mode 100644
index 62cac9bbbd3..00000000000
--- a/changelogs/unreleased/27354-navigation-new-button.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Re-add the New Project button in nav bar
-merge_request:
-author:
diff --git a/changelogs/unreleased/27376-cache-default-branch-pipeline-on-project.yml b/changelogs/unreleased/27376-cache-default-branch-pipeline-on-project.yml
deleted file mode 100644
index a116c68ad87..00000000000
--- a/changelogs/unreleased/27376-cache-default-branch-pipeline-on-project.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Speed up project dashboard by caching pipeline status and eager loading routes
-merge_request: 9903
-author:
diff --git a/changelogs/unreleased/27452-update-issue-count.yml b/changelogs/unreleased/27452-update-issue-count.yml
deleted file mode 100644
index a7417eba63c..00000000000
--- a/changelogs/unreleased/27452-update-issue-count.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: update issue count when closing/reopening an issue
-merge_request:
-author:
diff --git a/changelogs/unreleased/27501-api-use-visibility-everywhere.yml b/changelogs/unreleased/27501-api-use-visibility-everywhere.yml
deleted file mode 100644
index f1b70687878..00000000000
--- a/changelogs/unreleased/27501-api-use-visibility-everywhere.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "API: Use `visibility` as string parameter everywhere"
-merge_request: 9337
-author:
diff --git a/changelogs/unreleased/27503-feature-status-aria-labels.yml b/changelogs/unreleased/27503-feature-status-aria-labels.yml
new file mode 100644
index 00000000000..f514fd5b631
--- /dev/null
+++ b/changelogs/unreleased/27503-feature-status-aria-labels.yml
@@ -0,0 +1,4 @@
+---
+title: Add `aria-label` for feature status accessibility
+merge_request: 9830
+author:
diff --git a/changelogs/unreleased/27520-option-to-prevent-signing-in-from-multiple-ips.yml b/changelogs/unreleased/27520-option-to-prevent-signing-in-from-multiple-ips.yml
deleted file mode 100644
index 3050b072863..00000000000
--- a/changelogs/unreleased/27520-option-to-prevent-signing-in-from-multiple-ips.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Option to prevent signing in from multiple ips
-merge_request: 8998
-author:
diff --git a/changelogs/unreleased/27523-make-stuck-build-detection-more-performant.yml b/changelogs/unreleased/27523-make-stuck-build-detection-more-performant.yml
deleted file mode 100644
index a4ef2b23aaa..00000000000
--- a/changelogs/unreleased/27523-make-stuck-build-detection-more-performant.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make stuck builds detection more performant
-merge_request: 9025
-author:
diff --git a/changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml b/changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml
deleted file mode 100644
index 4436b4bee68..00000000000
--- a/changelogs/unreleased/27530-fix-job-dropdown-pipeline-console-error.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes job dropdown action throws error in js console
-merge_request: 9182
-author:
diff --git a/changelogs/unreleased/27532_api_changes.yml b/changelogs/unreleased/27532_api_changes.yml
deleted file mode 100644
index 778469d5a86..00000000000
--- a/changelogs/unreleased/27532_api_changes.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use iids as filter parameter
-merge_request: 9096
-author:
diff --git a/changelogs/unreleased/27568-refactor-very-slow-dropdown-asignee-spec.yml b/changelogs/unreleased/27568-refactor-very-slow-dropdown-asignee-spec.yml
deleted file mode 100644
index 5c738af7704..00000000000
--- a/changelogs/unreleased/27568-refactor-very-slow-dropdown-asignee-spec.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Refactor dropdown_assignee_spec
-merge_request: 9711
-author: George Andrinopoulos
diff --git a/changelogs/unreleased/27574-pipelines-empty-state.yml b/changelogs/unreleased/27574-pipelines-empty-state.yml
new file mode 100644
index 00000000000..c26ea97205f
--- /dev/null
+++ b/changelogs/unreleased/27574-pipelines-empty-state.yml
@@ -0,0 +1,4 @@
+---
+title: Adds empty and error state to pipelines
+merge_request:
+author:
diff --git a/changelogs/unreleased/27608-fixes-markdown-in-activity-feed-is-light-gray.yml b/changelogs/unreleased/27608-fixes-markdown-in-activity-feed-is-light-gray.yml
deleted file mode 100644
index 8f297620e23..00000000000
--- a/changelogs/unreleased/27608-fixes-markdown-in-activity-feed-is-light-gray.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes markdown in activity-feed is gray
-merge_request: 9179
-author:
diff --git a/changelogs/unreleased/27610-issue-number-alignment.yml b/changelogs/unreleased/27610-issue-number-alignment.yml
deleted file mode 100644
index 19ab8872c62..00000000000
--- a/changelogs/unreleased/27610-issue-number-alignment.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: fixes issue number alignment problem in MR and issue list
-merge_request: 9020
-author:
diff --git a/changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml b/changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml
deleted file mode 100644
index 59da28964f7..00000000000
--- a/changelogs/unreleased/27631-fix-small-height-of-activity-header-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "Fix small height of activity header page"
-merge_request: 8952
-author: Pavel Sorokin
diff --git a/changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml b/changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml
deleted file mode 100644
index 6c98b46d8cb..00000000000
--- a/changelogs/unreleased/27726-fix-dropdown-width-in-admin-project-page.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes dropdown width in admin project page
-merge_request: 9002
-author:
diff --git a/changelogs/unreleased/27762-add-default-artifacts-expiration.yml b/changelogs/unreleased/27762-add-default-artifacts-expiration.yml
deleted file mode 100644
index 27fa77ed04d..00000000000
--- a/changelogs/unreleased/27762-add-default-artifacts-expiration.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add admin setting for default artifacts expiration
-merge_request: 9219
-author:
diff --git a/changelogs/unreleased/27778-a11y-sidebar.yml b/changelogs/unreleased/27778-a11y-sidebar.yml
deleted file mode 100644
index fb37d7fdb35..00000000000
--- a/changelogs/unreleased/27778-a11y-sidebar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improves a11y in sidebar by adding aria-hidden attributes in i tags and by
- fixing two broken aria-hidden attributes
-merge_request:
-author:
diff --git a/changelogs/unreleased/27783-fix-fe-doc-broken-link.yml b/changelogs/unreleased/27783-fix-fe-doc-broken-link.yml
deleted file mode 100644
index 429110e9178..00000000000
--- a/changelogs/unreleased/27783-fix-fe-doc-broken-link.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes FE Doc broken link
-merge_request: 9120
-author:
diff --git a/changelogs/unreleased/27840-improve-search-bar-experience.yml b/changelogs/unreleased/27840-improve-search-bar-experience.yml
deleted file mode 100644
index 87b1f0c5572..00000000000
--- a/changelogs/unreleased/27840-improve-search-bar-experience.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Enhanced filter issues layout for better mobile experiance
-merge_request: 9280
-author: Pratik Borsadiya
diff --git a/changelogs/unreleased/27878-new-service-for-creating-user.yml b/changelogs/unreleased/27878-new-service-for-creating-user.yml
new file mode 100644
index 00000000000..c07f0cef8db
--- /dev/null
+++ b/changelogs/unreleased/27878-new-service-for-creating-user.yml
@@ -0,0 +1,4 @@
+---
+title: Implement user create service
+merge_request: 9220
+author: George Andrinopoulos
diff --git a/changelogs/unreleased/27910-admin-can-create-project-in-all-groups.yml b/changelogs/unreleased/27910-admin-can-create-project-in-all-groups.yml
new file mode 100644
index 00000000000..40fd8dacc82
--- /dev/null
+++ b/changelogs/unreleased/27910-admin-can-create-project-in-all-groups.yml
@@ -0,0 +1,4 @@
+---
+title: Allow admin to view all namespaces
+merge_request:
+author: George Andrinopoulos
diff --git a/changelogs/unreleased/27920-both-wip-messages-showing.yml b/changelogs/unreleased/27920-both-wip-messages-showing.yml
deleted file mode 100644
index 497fda8c8ba..00000000000
--- a/changelogs/unreleased/27920-both-wip-messages-showing.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Dispatch needed JS when creating a new MR in diff view
-merge_request:
-author:
diff --git a/changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml b/changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml
deleted file mode 100644
index 53077eedc11..00000000000
--- a/changelogs/unreleased/27924-set-max-width-mini-pipeline-text.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Set maximum width for mini pipeline graph text so it is not truncated to early
-merge_request: 9188
-author:
diff --git a/changelogs/unreleased/27934-left-align-logo.yml b/changelogs/unreleased/27934-left-align-logo.yml
deleted file mode 100644
index d4e5e169465..00000000000
--- a/changelogs/unreleased/27934-left-align-logo.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Left align logo
-merge_request:
-author:
diff --git a/changelogs/unreleased/27936-make-all-uploads-require-revalidation-on-each-browser-fetch.yml b/changelogs/unreleased/27936-make-all-uploads-require-revalidation-on-each-browser-fetch.yml
deleted file mode 100644
index adc129d8dca..00000000000
--- a/changelogs/unreleased/27936-make-all-uploads-require-revalidation-on-each-browser-fetch.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Uploaded files which content can change now require revalidation on each page load
-merge_request: 9453
-author:
diff --git a/changelogs/unreleased/27978-improve-task-list-ux.yml b/changelogs/unreleased/27978-improve-task-list-ux.yml
deleted file mode 100644
index a6bd99da82e..00000000000
--- a/changelogs/unreleased/27978-improve-task-list-ux.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Only add a newline in the Markdown Editor if the current line is not empty
-merge_request: 9455
-author: Jan Christophersen
diff --git a/changelogs/unreleased/27994-fix-mr-widget-jump.yml b/changelogs/unreleased/27994-fix-mr-widget-jump.yml
deleted file mode 100644
index 77783e54a3a..00000000000
--- a/changelogs/unreleased/27994-fix-mr-widget-jump.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix MR widget jump
-merge_request: 9146
-author:
diff --git a/changelogs/unreleased/28010-mr-merge-button-default-to-danger.yml b/changelogs/unreleased/28010-mr-merge-button-default-to-danger.yml
deleted file mode 100644
index 06bb669ceac..00000000000
--- a/changelogs/unreleased/28010-mr-merge-button-default-to-danger.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Default to subtle MR mege button until CI status is available
-merge_request:
-author:
diff --git a/changelogs/unreleased/28019-make-builds-show-faster.yml b/changelogs/unreleased/28019-make-builds-show-faster.yml
deleted file mode 100644
index bbfea0e4c88..00000000000
--- a/changelogs/unreleased/28019-make-builds-show-faster.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Avoid calling Build#trace_with_state for performance
-merge_request: 9149
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml b/changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml
deleted file mode 100644
index e0e826a67f8..00000000000
--- a/changelogs/unreleased/28058-hide-emails-in-atom-feeds.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Only show public emails in atom feeds
-merge_request:
-author:
diff --git a/changelogs/unreleased/28082-deleted-branch-event-404.yml b/changelogs/unreleased/28082-deleted-branch-event-404.yml
deleted file mode 100644
index e989ca34784..00000000000
--- a/changelogs/unreleased/28082-deleted-branch-event-404.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Stop linking to deleted Branches in Activity tabs
-merge_request: 9203
-author: Jan Christophersen
diff --git a/changelogs/unreleased/28142-overlap-bugs.yml b/changelogs/unreleased/28142-overlap-bugs.yml
deleted file mode 100644
index 9fdabdf204a..00000000000
--- a/changelogs/unreleased/28142-overlap-bugs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix z index issues with sidebar
-merge_request:
-author:
diff --git a/changelogs/unreleased/28176_merge_widget_fix.yml b/changelogs/unreleased/28176_merge_widget_fix.yml
deleted file mode 100644
index 8e4e75fc237..00000000000
--- a/changelogs/unreleased/28176_merge_widget_fix.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix error in MR widget after /merge slash command
-merge_request: 9259
-author:
diff --git a/changelogs/unreleased/28186-long-group-names-overflow-out-of-todos-view.yml b/changelogs/unreleased/28186-long-group-names-overflow-out-of-todos-view.yml
deleted file mode 100644
index 3bcf0e06d08..00000000000
--- a/changelogs/unreleased/28186-long-group-names-overflow-out-of-todos-view.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Truncate long Todo titles for non-mobile screens
-merge_request: 9311
-author:
diff --git a/changelogs/unreleased/28204-option-to-disable-webpack-dev-server-livereload.yml b/changelogs/unreleased/28204-option-to-disable-webpack-dev-server-livereload.yml
deleted file mode 100644
index df2478a3f28..00000000000
--- a/changelogs/unreleased/28204-option-to-disable-webpack-dev-server-livereload.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Pick up option from GDK to disable webpack dev server livereload
-merge_request:
-author:
diff --git a/changelogs/unreleased/28229-pipelines-loading-icon.yml b/changelogs/unreleased/28229-pipelines-loading-icon.yml
deleted file mode 100644
index d8f82f658c2..00000000000
--- a/changelogs/unreleased/28229-pipelines-loading-icon.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Centers loading icon vertically and horizontally in pipelines table in commit
- view
-merge_request:
-author:
diff --git a/changelogs/unreleased/28236-browse-button-dropping.yml b/changelogs/unreleased/28236-browse-button-dropping.yml
deleted file mode 100644
index 3a3d755f40c..00000000000
--- a/changelogs/unreleased/28236-browse-button-dropping.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Increase right side of file header to button stays on same line
-merge_request:
-author:
diff --git a/changelogs/unreleased/28247-timeloops-bug.yml b/changelogs/unreleased/28247-timeloops-bug.yml
deleted file mode 100644
index 12ab523b7c7..00000000000
--- a/changelogs/unreleased/28247-timeloops-bug.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Only run timeago loops after rendering timeago components
-merge_request:
-author:
diff --git a/changelogs/unreleased/28253-fix-buid-scroll-button-position.yml b/changelogs/unreleased/28253-fix-buid-scroll-button-position.yml
deleted file mode 100644
index b13d115dab9..00000000000
--- a/changelogs/unreleased/28253-fix-buid-scroll-button-position.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix positioning of `Scroll to top` button
-merge_request:
-author:
diff --git a/changelogs/unreleased/28257-issues-iids.yml b/changelogs/unreleased/28257-issues-iids.yml
deleted file mode 100644
index 0a85504a8de..00000000000
--- a/changelogs/unreleased/28257-issues-iids.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: API issues - support filtering by iids
-merge_request:
-author:
diff --git a/changelogs/unreleased/28262-horizontal-scrolling-issue-on-long-project-names.yml b/changelogs/unreleased/28262-horizontal-scrolling-issue-on-long-project-names.yml
deleted file mode 100644
index fa1674453de..00000000000
--- a/changelogs/unreleased/28262-horizontal-scrolling-issue-on-long-project-names.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Wrap long Project and Group titles
-merge_request: 9301
-author:
diff --git a/changelogs/unreleased/28277-document-u2f-limitations-with-multiple-urls.yml b/changelogs/unreleased/28277-document-u2f-limitations-with-multiple-urls.yml
deleted file mode 100644
index 6e3cd8a60d8..00000000000
--- a/changelogs/unreleased/28277-document-u2f-limitations-with-multiple-urls.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Document U2F limitations with multiple URLs
-merge_request: 9300
-author:
diff --git a/changelogs/unreleased/28303-change-development-tanuki-favicon-colors-to-match-logo.yml b/changelogs/unreleased/28303-change-development-tanuki-favicon-colors-to-match-logo.yml
deleted file mode 100644
index b97e9a59b2a..00000000000
--- a/changelogs/unreleased/28303-change-development-tanuki-favicon-colors-to-match-logo.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Change development tanuki favicon colors to match logo color order
-merge_request:
-author:
diff --git a/changelogs/unreleased/28329-allow-slash-in-slash-command-args.yml b/changelogs/unreleased/28329-allow-slash-in-slash-command-args.yml
deleted file mode 100644
index fed02139a5c..00000000000
--- a/changelogs/unreleased/28329-allow-slash-in-slash-command-args.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow slashes in slash command arguments
-merge_request:
-author:
diff --git a/changelogs/unreleased/28353-little-grammar-issue.yml b/changelogs/unreleased/28353-little-grammar-issue.yml
deleted file mode 100644
index 10bdb17b266..00000000000
--- a/changelogs/unreleased/28353-little-grammar-issue.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix grammer issue in admin/runners
-merge_request:
-author:
diff --git a/changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml b/changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml
deleted file mode 100644
index faf1e89ed94..00000000000
--- a/changelogs/unreleased/28366-renamed-file-tooltip-contains-html.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove markup that was showing in tooltip for renamed files
-merge_request: 9374
-author:
diff --git a/changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml b/changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml
deleted file mode 100644
index 6fc89fd91dd..00000000000
--- a/changelogs/unreleased/28367-fix-unfold-diff-line-number-copy-paste.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes includes line number during unfold copy n paste in parallel diff view
-merge_request: 9365
-author:
diff --git a/changelogs/unreleased/28389-ux-problem-with-pipeline-coverage-placeholder.yml b/changelogs/unreleased/28389-ux-problem-with-pipeline-coverage-placeholder.yml
deleted file mode 100644
index ed357d86fe3..00000000000
--- a/changelogs/unreleased/28389-ux-problem-with-pipeline-coverage-placeholder.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Changed coverage reg expression placeholder text to be more like a placeholder
-merge_request:
-author:
diff --git a/changelogs/unreleased/28410-dropdown-styling.yml b/changelogs/unreleased/28410-dropdown-styling.yml
deleted file mode 100644
index 2a7af1dd6e8..00000000000
--- a/changelogs/unreleased/28410-dropdown-styling.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add badges to global dropdown
-merge_request:
-author:
diff --git a/changelogs/unreleased/28447-hybrid-repository-storages.yml b/changelogs/unreleased/28447-hybrid-repository-storages.yml
deleted file mode 100644
index 00dfc5781b9..00000000000
--- a/changelogs/unreleased/28447-hybrid-repository-storages.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update storage settings to allow extra values per repository storage
-merge_request: 9597
-author:
diff --git a/changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml b/changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml
deleted file mode 100644
index 196a9b788ea..00000000000
--- a/changelogs/unreleased/28450-test-compiling-frontend-assets-for-production-in-ci.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: test compiling production assets and generate webpack bundle report in CI
-merge_request: 9396
-author:
diff --git a/changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml b/changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml
deleted file mode 100644
index dbbe8a19204..00000000000
--- a/changelogs/unreleased/28458-present-gitlab-version-for-v4-changes-on-docs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Present GitLab version for each V3 to V4 API change on v3_to_v4.md
-merge_request:
-author:
diff --git a/changelogs/unreleased/28462-fix-delimiter-removes-issue-in-todo-counter.yml b/changelogs/unreleased/28462-fix-delimiter-removes-issue-in-todo-counter.yml
deleted file mode 100644
index 80995d75c23..00000000000
--- a/changelogs/unreleased/28462-fix-delimiter-removes-issue-in-todo-counter.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes delimiter removes when todo marked as done
-merge_request: 9435
-author:
diff --git a/changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml b/changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml
deleted file mode 100644
index 660a881e094..00000000000
--- a/changelogs/unreleased/28499-fix-large-text-tooltip-in-diff-file-name.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes large file name tooltip cutoff in diff header
-merge_request: 9529
-author:
diff --git a/changelogs/unreleased/28516-default-kubernetes-namespace.yml b/changelogs/unreleased/28516-default-kubernetes-namespace.yml
deleted file mode 100644
index 9fa5c681a53..00000000000
--- a/changelogs/unreleased/28516-default-kubernetes-namespace.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make a default namespace of Kubernetes service to contain project ID
-merge_request:
-author:
diff --git a/changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml b/changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml
deleted file mode 100644
index eda5764c13e..00000000000
--- a/changelogs/unreleased/28524-gitlab-ci-yml-coverage-key-is-unknown.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Document when current coverage configuration option was introduced
-merge_request: 9443
-author:
diff --git a/changelogs/unreleased/28538-restore-nav-shortcuts.yml b/changelogs/unreleased/28538-restore-nav-shortcuts.yml
deleted file mode 100644
index 07b39cd50d1..00000000000
--- a/changelogs/unreleased/28538-restore-nav-shortcuts.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Restore keyboard shortcuts for "Activity" and "Charts"
-merge_request: 9680
-author:
diff --git a/changelogs/unreleased/28598-narrow-environment-payload-by-using-basic-project.yml b/changelogs/unreleased/28598-narrow-environment-payload-by-using-basic-project.yml
deleted file mode 100644
index ada726c9048..00000000000
--- a/changelogs/unreleased/28598-narrow-environment-payload-by-using-basic-project.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Narrow environment payload by using basic project details resource
-merge_request:
-author:
diff --git a/changelogs/unreleased/28614-harmonious-color-palette.yml b/changelogs/unreleased/28614-harmonious-color-palette.yml
new file mode 100644
index 00000000000..b436e7129a4
--- /dev/null
+++ b/changelogs/unreleased/28614-harmonious-color-palette.yml
@@ -0,0 +1,4 @@
+---
+title: Update color palette to a more harmonious and consistent one.
+merge_request: 10154
+author:
diff --git a/changelogs/unreleased/28634-todos-margin.yml b/changelogs/unreleased/28634-todos-margin.yml
new file mode 100644
index 00000000000..f4221ce4350
--- /dev/null
+++ b/changelogs/unreleased/28634-todos-margin.yml
@@ -0,0 +1,4 @@
+---
+title: Remove extra margin at bottom of todos page
+merge_request:
+author:
diff --git a/changelogs/unreleased/28655-current-path-text-is-not-updated-after-setting-the-new-username.yml b/changelogs/unreleased/28655-current-path-text-is-not-updated-after-setting-the-new-username.yml
deleted file mode 100644
index bff996172f3..00000000000
--- a/changelogs/unreleased/28655-current-path-text-is-not-updated-after-setting-the-new-username.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update account view to display new username
-merge_request:
-author:
diff --git a/changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml b/changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml
deleted file mode 100644
index e38e5d0db5b..00000000000
--- a/changelogs/unreleased/28696-improve-grammar-gitlab-flow-doc.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improve grammar in GitLab flow documentation
-merge_request: 9552
-author: infogrind
diff --git a/changelogs/unreleased/28704-fullscreen-zen-mode-is-broken.yml b/changelogs/unreleased/28704-fullscreen-zen-mode-is-broken.yml
deleted file mode 100644
index b8dba0b5993..00000000000
--- a/changelogs/unreleased/28704-fullscreen-zen-mode-is-broken.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Set max height to screen height for Zen mode
-merge_request: 9667
-author:
diff --git a/changelogs/unreleased/28713-fe-style-guide.yml b/changelogs/unreleased/28713-fe-style-guide.yml
new file mode 100644
index 00000000000..57edb43e27f
--- /dev/null
+++ b/changelogs/unreleased/28713-fe-style-guide.yml
@@ -0,0 +1,4 @@
+---
+title: Adds Frontend Styleguide to documentation
+merge_request: 9961
+author:
diff --git a/changelogs/unreleased/28723-consistent-handling-indexof.yml b/changelogs/unreleased/28723-consistent-handling-indexof.yml
deleted file mode 100644
index 95d6181d5fa..00000000000
--- a/changelogs/unreleased/28723-consistent-handling-indexof.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Keep consistent in handling indexOf results
-merge_request: 9531
-author: Takuya Noguchi
diff --git a/changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml b/changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml
deleted file mode 100644
index 38ff6b97b2b..00000000000
--- a/changelogs/unreleased/28805-download-archive-with-branch-like-feature-xxxx-add-extra-directory-level.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Ensure archive download is only one directory deep
-merge_request: 9616
-author:
diff --git a/changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml b/changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml
deleted file mode 100644
index 0016253e32e..00000000000
--- a/changelogs/unreleased/28807-search-for-milestone-by-title-in-rest-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Enable filtering milestones by search criteria in the API
-merge_request: 9606
-author:
diff --git a/changelogs/unreleased/28835-jobs-head.yml b/changelogs/unreleased/28835-jobs-head.yml
deleted file mode 100644
index 1580cfb19ba..00000000000
--- a/changelogs/unreleased/28835-jobs-head.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix jobs table header height
-merge_request:
-author:
diff --git a/changelogs/unreleased/28837-remove-help-duplicate.yml b/changelogs/unreleased/28837-remove-help-duplicate.yml
deleted file mode 100644
index b1001245663..00000000000
--- a/changelogs/unreleased/28837-remove-help-duplicate.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove help link from right dropdown
-merge_request:
-author:
diff --git a/changelogs/unreleased/28865-filter-by-authorized-projects-in-v4.yml b/changelogs/unreleased/28865-filter-by-authorized-projects-in-v4.yml
deleted file mode 100644
index 7c64783cbd0..00000000000
--- a/changelogs/unreleased/28865-filter-by-authorized-projects-in-v4.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add filter param for project membership for current_user in API v4
-merge_request:
-author:
diff --git a/changelogs/unreleased/28890-allow-creating-mr-without-target-branch-in-url.yml b/changelogs/unreleased/28890-allow-creating-mr-without-target-branch-in-url.yml
deleted file mode 100644
index 114a14ec2df..00000000000
--- a/changelogs/unreleased/28890-allow-creating-mr-without-target-branch-in-url.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow creating merge request even if target branch is not specified in query
- params
-merge_request: 9968
-author:
diff --git a/changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml b/changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml
deleted file mode 100644
index 9ba33af010c..00000000000
--- a/changelogs/unreleased/28893-highlighted-diff-doesn-t-stay-highlighted-on-refresh.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Highlight line number if specified on diff pages when page loads
-merge_request: 9664
-author:
diff --git a/changelogs/unreleased/28898-fix-search-branches-in-cherry-picking.yml b/changelogs/unreleased/28898-fix-search-branches-in-cherry-picking.yml
deleted file mode 100644
index 48e62f8f70d..00000000000
--- a/changelogs/unreleased/28898-fix-search-branches-in-cherry-picking.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix json response in branches controller
-merge_request: 9710
-author: George Andrinopoulos
diff --git a/changelogs/unreleased/28935-make-logo-smaller.yml b/changelogs/unreleased/28935-make-logo-smaller.yml
deleted file mode 100644
index ef79fc7d212..00000000000
--- a/changelogs/unreleased/28935-make-logo-smaller.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Decrease tanuki logo size
-merge_request:
-author:
diff --git a/changelogs/unreleased/29043-upgrade-vue-and-remove-warnings.yml b/changelogs/unreleased/29043-upgrade-vue-and-remove-warnings.yml
new file mode 100644
index 00000000000..9055b23a13f
--- /dev/null
+++ b/changelogs/unreleased/29043-upgrade-vue-and-remove-warnings.yml
@@ -0,0 +1,4 @@
+---
+title: Upgrade VueJS to v2.2.4 and disable dev mode warnings
+merge_request: 9981
+author:
diff --git a/changelogs/unreleased/29116-maxint-error.yml b/changelogs/unreleased/29116-maxint-error.yml
new file mode 100644
index 00000000000..06e976617d5
--- /dev/null
+++ b/changelogs/unreleased/29116-maxint-error.yml
@@ -0,0 +1,4 @@
+---
+title: Fix projects_limit RangeError on user create
+merge_request:
+author: Alexander Randa
diff --git a/changelogs/unreleased/29263-merge-button-color.yml b/changelogs/unreleased/29263-merge-button-color.yml
deleted file mode 100644
index 2d0625483a4..00000000000
--- a/changelogs/unreleased/29263-merge-button-color.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: ensure MR widget dropdown is same color as button
-merge_request:
-author:
diff --git a/changelogs/unreleased/29428-new-directory-from-existing-branch.yml b/changelogs/unreleased/29428-new-directory-from-existing-branch.yml
new file mode 100644
index 00000000000..b3f7cd1f8f8
--- /dev/null
+++ b/changelogs/unreleased/29428-new-directory-from-existing-branch.yml
@@ -0,0 +1,4 @@
+---
+title: New directory from interface on existing branch
+merge_request: 9921
+author: Jacopo Beschi @jacopo-beschi
diff --git a/changelogs/unreleased/29483-spam-check-only-title-and-description.yml b/changelogs/unreleased/29483-spam-check-only-title-and-description.yml
new file mode 100644
index 00000000000..de8cacb250d
--- /dev/null
+++ b/changelogs/unreleased/29483-spam-check-only-title-and-description.yml
@@ -0,0 +1,4 @@
+---
+title: Spam check only when spammable attributes have changed
+merge_request:
+author:
diff --git a/changelogs/unreleased/29550-fix-quick-submit-on-preview.yml b/changelogs/unreleased/29550-fix-quick-submit-on-preview.yml
new file mode 100644
index 00000000000..71214971ffd
--- /dev/null
+++ b/changelogs/unreleased/29550-fix-quick-submit-on-preview.yml
@@ -0,0 +1,4 @@
+---
+title: Fix quick submit short-cut on preview tab for comments
+merge_request: 10002
+author:
diff --git a/changelogs/unreleased/29555-align-all-todo.yml b/changelogs/unreleased/29555-align-all-todo.yml
new file mode 100644
index 00000000000..c1555a96a92
--- /dev/null
+++ b/changelogs/unreleased/29555-align-all-todo.yml
@@ -0,0 +1,4 @@
+---
+title: align Mark all as done with other Done buttons on Todos page
+merge_request:
+author:
diff --git a/changelogs/unreleased/29565-name-of-the-uncompressed-folder-of-a-tag-archive-changed.yml b/changelogs/unreleased/29565-name-of-the-uncompressed-folder-of-a-tag-archive-changed.yml
deleted file mode 100644
index d0a04b0a130..00000000000
--- a/changelogs/unreleased/29565-name-of-the-uncompressed-folder-of-a-tag-archive-changed.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix archive prefix bug for refs containing dots
-merge_request:
-author:
diff --git a/changelogs/unreleased/29575-polling.yml b/changelogs/unreleased/29575-polling.yml
new file mode 100644
index 00000000000..75016afd455
--- /dev/null
+++ b/changelogs/unreleased/29575-polling.yml
@@ -0,0 +1,4 @@
+---
+title: Adds polling utility function for vue resource
+merge_request:
+author:
diff --git a/changelogs/unreleased/29604-v3-fix-branch-creation.yml b/changelogs/unreleased/29604-v3-fix-branch-creation.yml
deleted file mode 100644
index 25687e8be97..00000000000
--- a/changelogs/unreleased/29604-v3-fix-branch-creation.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use "branch_name" instead "branch" on V3 branch creation API
-merge_request:
-author:
diff --git a/changelogs/unreleased/29897-remove-force-scroll-for-mr-changes-diff.yml b/changelogs/unreleased/29897-remove-force-scroll-for-mr-changes-diff.yml
new file mode 100644
index 00000000000..d1da96096f8
--- /dev/null
+++ b/changelogs/unreleased/29897-remove-force-scroll-for-mr-changes-diff.yml
@@ -0,0 +1,4 @@
+---
+title: Remove forced scroll into view when switching to Changes MR tab
+merge_request:
+author:
diff --git a/changelogs/unreleased/29930-fix-profile-cover-button-a11y.yml b/changelogs/unreleased/29930-fix-profile-cover-button-a11y.yml
new file mode 100644
index 00000000000..754d471c7d7
--- /dev/null
+++ b/changelogs/unreleased/29930-fix-profile-cover-button-a11y.yml
@@ -0,0 +1,4 @@
+---
+title: Add tooltip and accessibility for profile cover buttons
+merge_request: 10182
+author:
diff --git a/changelogs/unreleased/30035-milestone-with-due-date-shows-escaped-html.yml b/changelogs/unreleased/30035-milestone-with-due-date-shows-escaped-html.yml
new file mode 100644
index 00000000000..651c299ac66
--- /dev/null
+++ b/changelogs/unreleased/30035-milestone-with-due-date-shows-escaped-html.yml
@@ -0,0 +1,4 @@
+---
+title: Fix escaped html appearing in milestone page
+merge_request: 10224
+author:
diff --git a/changelogs/unreleased/3440-remove-hsts-header.yml b/changelogs/unreleased/3440-remove-hsts-header.yml
deleted file mode 100644
index 0310e733f4e..00000000000
--- a/changelogs/unreleased/3440-remove-hsts-header.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Stop setting Strict-Transport-Securty header from within the app
-merge_request:
-author:
diff --git a/changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml b/changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml
deleted file mode 100644
index 4a4932288b4..00000000000
--- a/changelogs/unreleased/3874-correctly-return-json-on-delete-responses.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Return 202 with JSON body on async removals on V4 API
-merge_request:
-author:
diff --git a/changelogs/unreleased/4195-add-sorting-to-project-milestones.yml b/changelogs/unreleased/4195-add-sorting-to-project-milestones.yml
new file mode 100644
index 00000000000..d4104dfa772
--- /dev/null
+++ b/changelogs/unreleased/4195-add-sorting-to-project-milestones.yml
@@ -0,0 +1,4 @@
+---
+title: Add dropdown sort to project milestones
+merge_request:
+author: George Andrinopoulos
diff --git a/changelogs/unreleased/6073_project_api.yml b/changelogs/unreleased/6073_project_api.yml
deleted file mode 100644
index fd6792a406e..00000000000
--- a/changelogs/unreleased/6073_project_api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API project create: Make name or path required'
-merge_request: 9416
-author:
diff --git a/changelogs/unreleased/9381-authentiq-backchannel-logout.yml b/changelogs/unreleased/9381-authentiq-backchannel-logout.yml
deleted file mode 100644
index 4dbf36cd096..00000000000
--- a/changelogs/unreleased/9381-authentiq-backchannel-logout.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Adds remote logout functionality to the Authentiq OAuth provider
-merge_request: 9381
-author: Alexandros Keramidas
diff --git a/changelogs/unreleased/add-auto-submited-header.yml b/changelogs/unreleased/add-auto-submited-header.yml
deleted file mode 100644
index 93481613b39..00000000000
--- a/changelogs/unreleased/add-auto-submited-header.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Set Auto-Submitted header to mails
-merge_request:
-author: Semyon Pupkov
diff --git a/changelogs/unreleased/add-changelog-filtered-search-visual-tokens.yml b/changelogs/unreleased/add-changelog-filtered-search-visual-tokens.yml
deleted file mode 100644
index d10e4cb7c87..00000000000
--- a/changelogs/unreleased/add-changelog-filtered-search-visual-tokens.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add filtered search visual tokens
-merge_request: 8969
-author:
diff --git a/changelogs/unreleased/add-filtered-search-to-mr.yml b/changelogs/unreleased/add-filtered-search-to-mr.yml
deleted file mode 100644
index e3577e2aec7..00000000000
--- a/changelogs/unreleased/add-filtered-search-to-mr.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add filtered search to MR page
-merge_request:
-author:
diff --git a/changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml b/changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml
deleted file mode 100644
index 66d5bb63734..00000000000
--- a/changelogs/unreleased/add-frequently-used-emojis-back-to-menu.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add frequently used emojis back to awards menu
-merge_request:
-author:
diff --git a/changelogs/unreleased/add-git-version-to-system-info.yml b/changelogs/unreleased/add-git-version-to-system-info.yml
deleted file mode 100644
index 2827fcec28d..00000000000
--- a/changelogs/unreleased/add-git-version-to-system-info.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add git version to gitlab:env:info
-merge_request: 9128
-author: Semyon Pupkov
diff --git a/changelogs/unreleased/add-issue-modal-loading-indicator.yml b/changelogs/unreleased/add-issue-modal-loading-indicator.yml
new file mode 100644
index 00000000000..5398399c018
--- /dev/null
+++ b/changelogs/unreleased/add-issue-modal-loading-indicator.yml
@@ -0,0 +1,4 @@
+---
+title: Shows loading icon in issue boards modal when changing filters
+merge_request:
+author:
diff --git a/changelogs/unreleased/add-kube-ca-pem-file-deprecate-kube-ca-pem.yml b/changelogs/unreleased/add-kube-ca-pem-file-deprecate-kube-ca-pem.yml
deleted file mode 100644
index 1ae1e3c7a7a..00000000000
--- a/changelogs/unreleased/add-kube-ca-pem-file-deprecate-kube-ca-pem.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add KUBE_CA_PEM_FILE, deprecate KUBE_CA_PEM
-merge_request: 9398
-author:
diff --git a/changelogs/unreleased/add-pipeline-triggers.yml b/changelogs/unreleased/add-pipeline-triggers.yml
deleted file mode 100644
index 81b11da0bb2..00000000000
--- a/changelogs/unreleased/add-pipeline-triggers.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add pipeline trigger API with user permissions
-merge_request: 9277
-author:
diff --git a/changelogs/unreleased/add-test-backoff-util.yml b/changelogs/unreleased/add-test-backoff-util.yml
new file mode 100644
index 00000000000..f3f3b99caec
--- /dev/null
+++ b/changelogs/unreleased/add-test-backoff-util.yml
@@ -0,0 +1,4 @@
+---
+title: Added tests for the w.gl.utils.backOff promise
+merge_request:
+author:
diff --git a/changelogs/unreleased/add-todos-shortcut.yml b/changelogs/unreleased/add-todos-shortcut.yml
new file mode 100644
index 00000000000..41d42775937
--- /dev/null
+++ b/changelogs/unreleased/add-todos-shortcut.yml
@@ -0,0 +1,4 @@
+---
+title: Add `g t` global shortcut to go to todos
+merge_request:
+author:
diff --git a/changelogs/unreleased/add-yarn-documentation.yml b/changelogs/unreleased/add-yarn-documentation.yml
deleted file mode 100644
index 5bcc01ac177..00000000000
--- a/changelogs/unreleased/add-yarn-documentation.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: add rake tasks to handle yarn dependencies and update documentation
-merge_request: 9316
-author:
diff --git a/changelogs/unreleased/add_mr_info_to_issues_list.yml b/changelogs/unreleased/add_mr_info_to_issues_list.yml
deleted file mode 100644
index 8087aa6296c..00000000000
--- a/changelogs/unreleased/add_mr_info_to_issues_list.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add merge request count to each issue on issues list
-merge_request: 9252
-author: blackst0ne
diff --git a/changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml b/changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml
deleted file mode 100644
index ffcf197a596..00000000000
--- a/changelogs/unreleased/alphabetically_sort_tags_on_runner_list.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Alphabetically sort tags on runner list
-merge_request: 8922
-author: blackst0ne
diff --git a/changelogs/unreleased/api-drop-subscribed.yml b/changelogs/unreleased/api-drop-subscribed.yml
deleted file mode 100644
index 2a39026b519..00000000000
--- a/changelogs/unreleased/api-drop-subscribed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove "subscribed" field from API responses returning list of issues or merge
- requests
-merge_request: 9661
-author:
diff --git a/changelogs/unreleased/api-empty-return.yml b/changelogs/unreleased/api-empty-return.yml
deleted file mode 100644
index 7810e83eb0e..00000000000
--- a/changelogs/unreleased/api-empty-return.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Return 204 for all delete endpoints'
-merge_request: 9397
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-entities.yml b/changelogs/unreleased/api-entities.yml
deleted file mode 100644
index 2003d00fd52..00000000000
--- a/changelogs/unreleased/api-entities.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: "Use an entity for RepoBranch commits and enhance RepoCommit"
-merge_request: 7138
-author: Ben Boeckel
diff --git a/changelogs/unreleased/api-notes-entity-fields.yml b/changelogs/unreleased/api-notes-entity-fields.yml
deleted file mode 100644
index f7631df31e2..00000000000
--- a/changelogs/unreleased/api-notes-entity-fields.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Remove deprecated fields Notes#upvotes and Notes#downvotes'
-merge_request: 9384
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-post-block.yml b/changelogs/unreleased/api-post-block.yml
deleted file mode 100644
index dfc61ffa9e3..00000000000
--- a/changelogs/unreleased/api-post-block.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Use POST to (un)block a user'
-merge_request: 9371
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-project-issues-404.yml b/changelogs/unreleased/api-project-issues-404.yml
deleted file mode 100644
index ce40395c99e..00000000000
--- a/changelogs/unreleased/api-project-issues-404.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Return 404 in project issues API endpoint when project cannot be found
-merge_request: 10093
-author:
diff --git a/changelogs/unreleased/api-remove-deploy-key-disable.yml b/changelogs/unreleased/api-remove-deploy-key-disable.yml
deleted file mode 100644
index f471ad2aa20..00000000000
--- a/changelogs/unreleased/api-remove-deploy-key-disable.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Remove `DELETE projects/:id/deploy_keys/:key_id/disable`'
-merge_request: 9365
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-remove-owned-groups.yml b/changelogs/unreleased/api-remove-owned-groups.yml
deleted file mode 100644
index cf0301b7fe0..00000000000
--- a/changelogs/unreleased/api-remove-owned-groups.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Remove /groups/owned endpoint'
-merge_request: 9505
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-star-restful.yml b/changelogs/unreleased/api-star-restful.yml
deleted file mode 100644
index 3e7de8cd822..00000000000
--- a/changelogs/unreleased/api-star-restful.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Moved `DELETE /projects/:id/star` to `POST /projects/:id/unstar`'
-merge_request: 9328
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-subscription-restful.yml b/changelogs/unreleased/api-subscription-restful.yml
deleted file mode 100644
index 95db470e6c9..00000000000
--- a/changelogs/unreleased/api-subscription-restful.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: - Make subscription API more RESTful. Use `post ":project_id/:subscribable_type/:subscribable_id/subscribe"` to subscribe and `post ":project_id/:subscribable_type/:subscribable_id/unsubscribe"` to unsubscribe from a resource.'
-merge_request: 9325
-author: Robert Schilling
diff --git a/changelogs/unreleased/api-todos-restful.yml b/changelogs/unreleased/api-todos-restful.yml
deleted file mode 100644
index dba1350a495..00000000000
--- a/changelogs/unreleased/api-todos-restful.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Use POST requests to mark todos as done'
-merge_request: 9410
-author: Robert Schilling
diff --git a/changelogs/unreleased/artifactsdoc.yml b/changelogs/unreleased/artifactsdoc.yml
deleted file mode 100644
index 4ef32d5256f..00000000000
--- a/changelogs/unreleased/artifactsdoc.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added documentation for permalinks to most recent build artifacts.
-merge_request: 8934
-author: Christian Godenschwager
diff --git a/changelogs/unreleased/backup_storage_class.yml b/changelogs/unreleased/backup_storage_class.yml
deleted file mode 100644
index fc9989fc251..00000000000
--- a/changelogs/unreleased/backup_storage_class.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add storage class configuration option for Amazon S3 remote backups
-merge_request:
-author: Jon Keys
diff --git a/changelogs/unreleased/beautiful-karma-output.yml b/changelogs/unreleased/beautiful-karma-output.yml
deleted file mode 100644
index 6ccddebab68..00000000000
--- a/changelogs/unreleased/beautiful-karma-output.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make Karma output look nicer for CI
-merge_request: 9165
-author: winniehell
diff --git a/changelogs/unreleased/branch_deletion.yml b/changelogs/unreleased/branch_deletion.yml
deleted file mode 100644
index dbc9265a1fb..00000000000
--- a/changelogs/unreleased/branch_deletion.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: on branch deletion show loading icon and disabled the button
-merge_request: 6761
-author: wendy0402
diff --git a/changelogs/unreleased/bypass-email-domain-validation-when-created-by-admin.yml b/changelogs/unreleased/bypass-email-domain-validation-when-created-by-admin.yml
deleted file mode 100644
index f335ae27fda..00000000000
--- a/changelogs/unreleased/bypass-email-domain-validation-when-created-by-admin.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Bypass email domain validation when a user is created by an admin.
-merge_request: 8575
-author: Reza Mohammadi @remohammadi
diff --git a/changelogs/unreleased/cleaner-additional-award-emoji-button.yml b/changelogs/unreleased/cleaner-additional-award-emoji-button.yml
new file mode 100644
index 00000000000..84685f4bd45
--- /dev/null
+++ b/changelogs/unreleased/cleaner-additional-award-emoji-button.yml
@@ -0,0 +1,4 @@
+---
+title: Removed unnecessary 'add' text in additional award emoji button
+merge_request:
+author:
diff --git a/changelogs/unreleased/clear-connections-before-starting-sidekiq.yml b/changelogs/unreleased/clear-connections-before-starting-sidekiq.yml
deleted file mode 100644
index 8778fac6e9d..00000000000
--- a/changelogs/unreleased/clear-connections-before-starting-sidekiq.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Clear ActiveRecord connections before starting Sidekiq
-merge_request:
-author:
diff --git a/changelogs/unreleased/commons-chunk-plugin.yml b/changelogs/unreleased/commons-chunk-plugin.yml
deleted file mode 100644
index 5c11ea3bbb2..00000000000
--- a/changelogs/unreleased/commons-chunk-plugin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use webpack CommonsChunkPlugin to place common javascript libraries in their
- own bundles
-merge_request: 9647
-author:
diff --git a/changelogs/unreleased/copy-branch-to-clipboard.yml b/changelogs/unreleased/copy-branch-to-clipboard.yml
deleted file mode 100644
index c12e324ed3c..00000000000
--- a/changelogs/unreleased/copy-branch-to-clipboard.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added the ability to copy a branch name to the clipboard
-merge_request: 9103
-author: Glenn Sayers
diff --git a/changelogs/unreleased/cover-my-karma.yml b/changelogs/unreleased/cover-my-karma.yml
deleted file mode 100644
index 4a823dc5ca4..00000000000
--- a/changelogs/unreleased/cover-my-karma.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Reintroduce coverage report for JavaScript
-merge_request: 9133
-author: winniehell
diff --git a/changelogs/unreleased/create_branch_repo_less.yml b/changelogs/unreleased/create_branch_repo_less.yml
deleted file mode 100644
index e8b14fa3b67..00000000000
--- a/changelogs/unreleased/create_branch_repo_less.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Creating a new branch from an issue will automatically initialize a repository if one doesn't already exist.
-merge_request:
-author:
diff --git a/changelogs/unreleased/dashboard-filter-search-keep-params.yml b/changelogs/unreleased/dashboard-filter-search-keep-params.yml
deleted file mode 100644
index a140715b7a2..00000000000
--- a/changelogs/unreleased/dashboard-filter-search-keep-params.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Dashboard project search keeps selected sort & filters
-merge_request:
-author:
diff --git a/changelogs/unreleased/delete-artifacts-for-pages.yml b/changelogs/unreleased/delete-artifacts-for-pages.yml
deleted file mode 100644
index 50b3dd81d60..00000000000
--- a/changelogs/unreleased/delete-artifacts-for-pages.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Delete artifacts for pages unless expiry date is specified
-merge_request: 9716
-author:
diff --git a/changelogs/unreleased/diff-make-obvious-cant-comment.yml b/changelogs/unreleased/diff-make-obvious-cant-comment.yml
deleted file mode 100644
index 2cb95947939..00000000000
--- a/changelogs/unreleased/diff-make-obvious-cant-comment.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Visually show expanded diff lines cant have comments
-merge_request:
-author:
diff --git a/changelogs/unreleased/dm-group-reference-full-name.yml b/changelogs/unreleased/dm-group-reference-full-name.yml
deleted file mode 100644
index f445d955529..00000000000
--- a/changelogs/unreleased/dm-group-reference-full-name.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use full group name in GFM group reference title
-merge_request:
-author:
diff --git a/changelogs/unreleased/dynamic-header-fixture.yml b/changelogs/unreleased/dynamic-header-fixture.yml
deleted file mode 100644
index 9789a1999c8..00000000000
--- a/changelogs/unreleased/dynamic-header-fixture.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace static fixture for header_spec.js
-merge_request: 9174
-author: winniehell
diff --git a/changelogs/unreleased/dynamic-project-title-fixture.yml b/changelogs/unreleased/dynamic-project-title-fixture.yml
deleted file mode 100644
index 2404cbb891c..00000000000
--- a/changelogs/unreleased/dynamic-project-title-fixture.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace static fixture for project_title_spec.js
-merge_request: 9175
-author: winniehell
diff --git a/changelogs/unreleased/dz-blacklist--names.yml b/changelogs/unreleased/dz-blacklist--names.yml
deleted file mode 100644
index 2941965002d..00000000000
--- a/changelogs/unreleased/dz-blacklist--names.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Reserve few project and nested group paths that have wildcard routes associated
-merge_request: 9898
-author:
diff --git a/changelogs/unreleased/dz-change-project-view.yml b/changelogs/unreleased/dz-change-project-view.yml
deleted file mode 100644
index 47e007a80a8..00000000000
--- a/changelogs/unreleased/dz-change-project-view.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Change default project view for user from readme to files view
-merge_request: 9584
-author:
diff --git a/changelogs/unreleased/dz-create-nested-groups-via-ui.yml b/changelogs/unreleased/dz-create-nested-groups-via-ui.yml
deleted file mode 100644
index f9529a5941a..00000000000
--- a/changelogs/unreleased/dz-create-nested-groups-via-ui.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Allow creating nested groups via UI
-merge_request: 8786
-author:
diff --git a/changelogs/unreleased/dz-dashboard-groups-search.yml b/changelogs/unreleased/dz-dashboard-groups-search.yml
deleted file mode 100644
index c473cba774d..00000000000
--- a/changelogs/unreleased/dz-dashboard-groups-search.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add filter and sorting to dashboard groups page
-merge_request: 9619
-author:
diff --git a/changelogs/unreleased/dz-nested-groups-api.yml b/changelogs/unreleased/dz-nested-groups-api.yml
deleted file mode 100644
index d33ff42700f..00000000000
--- a/changelogs/unreleased/dz-nested-groups-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add nested groups to the API
-merge_request: 9034
-author:
diff --git a/changelogs/unreleased/dz-nested-groups-members.yml b/changelogs/unreleased/dz-nested-groups-members.yml
deleted file mode 100644
index bab0c8465c2..00000000000
--- a/changelogs/unreleased/dz-nested-groups-members.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Show members of parent groups on project members page
-merge_request:
-author:
diff --git a/changelogs/unreleased/dz-nested-groups-restrictions.yml b/changelogs/unreleased/dz-nested-groups-restrictions.yml
deleted file mode 100644
index 2ffb6032525..00000000000
--- a/changelogs/unreleased/dz-nested-groups-restrictions.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Restrict nested group names to prevent ambiguous routes
-merge_request: 9738
-author:
diff --git a/changelogs/unreleased/dz-refactor-full-path.yml b/changelogs/unreleased/dz-refactor-full-path.yml
deleted file mode 100644
index da8568fd220..00000000000
--- a/changelogs/unreleased/dz-refactor-full-path.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Store group and project full name and full path in routes table
-merge_request: 8979
-author:
diff --git a/changelogs/unreleased/etag-notes-polling.yml b/changelogs/unreleased/etag-notes-polling.yml
deleted file mode 100644
index 53990821d25..00000000000
--- a/changelogs/unreleased/etag-notes-polling.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use ETag to improve performance of issue notes polling
-merge_request: 9036
-author:
diff --git a/changelogs/unreleased/expose-pagination-headers.yml b/changelogs/unreleased/expose-pagination-headers.yml
deleted file mode 100644
index 1b4cd43fa06..00000000000
--- a/changelogs/unreleased/expose-pagination-headers.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'CORS: Whitelist pagination headers'
-merge_request: 9651
-author: Robert Schilling
diff --git a/changelogs/unreleased/fe-paginated-environments-api-add-subview.yml b/changelogs/unreleased/fe-paginated-environments-api-add-subview.yml
deleted file mode 100644
index 7e626982de6..00000000000
--- a/changelogs/unreleased/fe-paginated-environments-api-add-subview.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Adds paginationd and folders view to environments table
-merge_request:
-author:
diff --git a/changelogs/unreleased/feature-brand-logo-in-emails.yml b/changelogs/unreleased/feature-brand-logo-in-emails.yml
deleted file mode 100644
index a7674b9b25e..00000000000
--- a/changelogs/unreleased/feature-brand-logo-in-emails.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Brand header logo for pipeline emails
-merge_request: 9049
-author: Alexis Reigel
diff --git a/changelogs/unreleased/feature-github-find-users-by-email.yml b/changelogs/unreleased/feature-github-find-users-by-email.yml
deleted file mode 100644
index 1503cf2b9f7..00000000000
--- a/changelogs/unreleased/feature-github-find-users-by-email.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: GitHub Importer - Find users based on GitHub email address
-merge_request: 8958
-author:
diff --git a/changelogs/unreleased/feature-openid-connect.yml b/changelogs/unreleased/feature-openid-connect.yml
deleted file mode 100644
index e84eb7aff86..00000000000
--- a/changelogs/unreleased/feature-openid-connect.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Implement OpenID Connect identity provider
-merge_request: 8018
-author: Markus Koller
diff --git a/changelogs/unreleased/feature-runner-jobs-v4-api.yml b/changelogs/unreleased/feature-runner-jobs-v4-api.yml
deleted file mode 100644
index b24ea65266d..00000000000
--- a/changelogs/unreleased/feature-runner-jobs-v4-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Runner's jobs v4 API
-merge_request: 9273
-author:
diff --git a/changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml b/changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml
deleted file mode 100644
index e646a6a17b7..00000000000
--- a/changelogs/unreleased/feature-runners-registration-deletion-v4-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Runner's registration/deletion v4 API
-merge_request: 9246
-author:
diff --git a/changelogs/unreleased/feature-syshook_commits.yml b/changelogs/unreleased/feature-syshook_commits.yml
deleted file mode 100644
index 1305f5cd414..00000000000
--- a/changelogs/unreleased/feature-syshook_commits.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added commit array to Syshook json
-merge_request: 9685
-author: Gabriele Pongelli
diff --git a/changelogs/unreleased/feature-tokens-rake-task.yml b/changelogs/unreleased/feature-tokens-rake-task.yml
new file mode 100644
index 00000000000..6c3845757db
--- /dev/null
+++ b/changelogs/unreleased/feature-tokens-rake-task.yml
@@ -0,0 +1,4 @@
+---
+title: New rake task to reset all email and private tokens
+merge_request:
+author:
diff --git a/changelogs/unreleased/filter-bar-fix-ie.yml b/changelogs/unreleased/filter-bar-fix-ie.yml
new file mode 100644
index 00000000000..f1fa7d9b177
--- /dev/null
+++ b/changelogs/unreleased/filter-bar-fix-ie.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed filtered search not working in IE
+merge_request:
+author:
diff --git a/changelogs/unreleased/fix-ci-api-regression-for-after-script.yml b/changelogs/unreleased/fix-ci-api-regression-for-after-script.yml
new file mode 100644
index 00000000000..cdd7d1e6945
--- /dev/null
+++ b/changelogs/unreleased/fix-ci-api-regression-for-after-script.yml
@@ -0,0 +1,4 @@
+---
+title: Fix after_script processing for Runners APIv4
+merge_request: 10185
+author:
diff --git a/changelogs/unreleased/fix-cycle-analytics-events-limit.yml b/changelogs/unreleased/fix-cycle-analytics-events-limit.yml
deleted file mode 100644
index 152b37ca430..00000000000
--- a/changelogs/unreleased/fix-cycle-analytics-events-limit.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add limit to the number of events showed in cycle analytics
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-gb-deprecate-ci-config-types.yml b/changelogs/unreleased/fix-gb-deprecate-ci-config-types.yml
deleted file mode 100644
index 605b5f01d0e..00000000000
--- a/changelogs/unreleased/fix-gb-deprecate-ci-config-types.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Deprecate usage of `types` configuration entry to describe CI/CD stages
-merge_request: 9766
-author:
diff --git a/changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml b/changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml
deleted file mode 100644
index 17fd1336b8e..00000000000
--- a/changelogs/unreleased/fix-gb-notification-settings-when-no-repository.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Show notifications settings dropdown even if repository feature is disabled
-merge_request: 9180
-author:
diff --git a/changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml b/changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml
deleted file mode 100644
index 6365b1a1910..00000000000
--- a/changelogs/unreleased/fix-gb-passed-with-warnings-status-on-mysql.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix "passed with warnings" stage status on MySQL installations
-merge_request: 9802
-author:
diff --git a/changelogs/unreleased/fix-gb-pipeline-retry-builds-started.yml b/changelogs/unreleased/fix-gb-pipeline-retry-builds-started.yml
deleted file mode 100644
index 49e243ca6bb..00000000000
--- a/changelogs/unreleased/fix-gb-pipeline-retry-builds-started.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix CI/CD pipeline retry and take stages order into account
-merge_request: 9021
-author:
diff --git a/changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml b/changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml
deleted file mode 100644
index d747e0e63a3..00000000000
--- a/changelogs/unreleased/fix-gb-pipeline-retry-cancel-buttons-consistency.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix pipeline retry and cancel buttons on pipeline details page
-merge_request: 9225
-author:
diff --git a/changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml b/changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml
deleted file mode 100644
index 71ff768a190..00000000000
--- a/changelogs/unreleased/fix-gb-remove-deprecated-ci-build-status-badge.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove deprecated build status badge and related services
-merge_request: 9620
-author:
diff --git a/changelogs/unreleased/fix-gb-update-commit-status-api.yml b/changelogs/unreleased/fix-gb-update-commit-status-api.yml
deleted file mode 100644
index aa4fcba4e89..00000000000
--- a/changelogs/unreleased/fix-gb-update-commit-status-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix updaing commit status when using optional attributes
-merge_request: 9618
-author:
diff --git a/changelogs/unreleased/fix-issue-23237.yml b/changelogs/unreleased/fix-issue-23237.yml
new file mode 100644
index 00000000000..ed0ffc0684d
--- /dev/null
+++ b/changelogs/unreleased/fix-issue-23237.yml
@@ -0,0 +1,4 @@
+---
+title: "Fixes an issue in the new merge request form, where a tag would be selected instead of a branch when they have the same names"
+merge_request: 9535
+author: Weiqing Chu
diff --git a/changelogs/unreleased/fix-mentioned-issues-for-external-trackers.yml b/changelogs/unreleased/fix-mentioned-issues-for-external-trackers.yml
deleted file mode 100644
index ee827b7c939..00000000000
--- a/changelogs/unreleased/fix-mentioned-issues-for-external-trackers.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix issues mentioned but not closed for external issue trackers
-merge_request:
-author:
diff --git a/changelogs/unreleased/fix-prometheus-including-d3-main-bundle.yml b/changelogs/unreleased/fix-prometheus-including-d3-main-bundle.yml
deleted file mode 100644
index a42b0db3cfc..00000000000
--- a/changelogs/unreleased/fix-prometheus-including-d3-main-bundle.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Removed d3 from the main application.js bundle
-merge_request: 10062
-author:
diff --git a/changelogs/unreleased/fix-slow-queries-for-branches-index.yml b/changelogs/unreleased/fix-slow-queries-for-branches-index.yml
deleted file mode 100644
index f5bd7003615..00000000000
--- a/changelogs/unreleased/fix-slow-queries-for-branches-index.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixes n+1 query for tags and branches index page
-merge_request: 9905
-author:
diff --git a/changelogs/unreleased/fix_issue_from_milestone.yml b/changelogs/unreleased/fix_issue_from_milestone.yml
deleted file mode 100644
index 02581e3ea09..00000000000
--- a/changelogs/unreleased/fix_issue_from_milestone.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: fix milestone does not automatically assign when create issue from milestone
-merge_request:
-author:
diff --git a/changelogs/unreleased/fixes-namespace-api-documentation.yml b/changelogs/unreleased/fixes-namespace-api-documentation.yml
deleted file mode 100644
index 6b578bb1602..00000000000
--- a/changelogs/unreleased/fixes-namespace-api-documentation.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update API docs for new namespace format
-merge_request: 9073
-author: Markus Koller
diff --git a/changelogs/unreleased/format-timeago-date.yml b/changelogs/unreleased/format-timeago-date.yml
deleted file mode 100644
index f331c34abbc..00000000000
--- a/changelogs/unreleased/format-timeago-date.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Format timeago date to short format
-merge_request:
-author:
diff --git a/changelogs/unreleased/gfm-autocomplete-fixes.yml b/changelogs/unreleased/gfm-autocomplete-fixes.yml
deleted file mode 100644
index 737e2ad5234..00000000000
--- a/changelogs/unreleased/gfm-autocomplete-fixes.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix errors in slash commands matcher, add simple test coverage
-merge_request:
-author: YarNayar
diff --git a/changelogs/unreleased/gitaly-post-receive.yml b/changelogs/unreleased/gitaly-post-receive.yml
deleted file mode 100644
index cf206e39084..00000000000
--- a/changelogs/unreleased/gitaly-post-receive.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add internal API to notify Gitaly of post receive
-merge_request: 8983
-author:
diff --git a/changelogs/unreleased/group-memebrs-owner-level.yml b/changelogs/unreleased/group-memebrs-owner-level.yml
deleted file mode 100644
index ba77f38eb6d..00000000000
--- a/changelogs/unreleased/group-memebrs-owner-level.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added option to update to owner for group members
-merge_request:
-author:
diff --git a/changelogs/unreleased/instrument-in-karma.yml b/changelogs/unreleased/instrument-in-karma.yml
deleted file mode 100644
index cfabf2569fe..00000000000
--- a/changelogs/unreleased/instrument-in-karma.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Move babel config for instanbul to karma config
-merge_request: 9286
-author: winniehell
diff --git a/changelogs/unreleased/introduce-pipeline-triggers.yml b/changelogs/unreleased/introduce-pipeline-triggers.yml
deleted file mode 100644
index ce5a230d48f..00000000000
--- a/changelogs/unreleased/introduce-pipeline-triggers.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Introduce Pipeline Triggers that are user-aware
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue-boards-cant-drag-fix.yml b/changelogs/unreleased/issue-boards-cant-drag-fix.yml
new file mode 100644
index 00000000000..ac92573abe8
--- /dev/null
+++ b/changelogs/unreleased/issue-boards-cant-drag-fix.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed bug in issue boards which stopped cards being able to be dragged
+merge_request:
+author:
diff --git a/changelogs/unreleased/issue-descrpiption-spinner-off.yml b/changelogs/unreleased/issue-descrpiption-spinner-off.yml
deleted file mode 100644
index 87104d09804..00000000000
--- a/changelogs/unreleased/issue-descrpiption-spinner-off.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed loading spinner position on issue template toggle
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue-newproj-layout.yml b/changelogs/unreleased/issue-newproj-layout.yml
deleted file mode 100644
index d15e8b7d1e5..00000000000
--- a/changelogs/unreleased/issue-newproj-layout.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Removed duplicate "Visibility Level" label on New Project page
-merge_request:
-author: Robert Marcano
diff --git a/changelogs/unreleased/issue-tags-layout.yml b/changelogs/unreleased/issue-tags-layout.yml
deleted file mode 100644
index abf4a609932..00000000000
--- a/changelogs/unreleased/issue-tags-layout.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix 'New Tag' layout on Tags page
-merge_request:
-author: Robert Marcano
diff --git a/changelogs/unreleased/issue_16834.yml b/changelogs/unreleased/issue_16834.yml
deleted file mode 100644
index 06175579ac3..00000000000
--- a/changelogs/unreleased/issue_16834.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update API endpoints for raw files
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_24815.yml b/changelogs/unreleased/issue_24815.yml
deleted file mode 100644
index 916e47d36a9..00000000000
--- a/changelogs/unreleased/issue_24815.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix issuable stale object error handler for js when updating tasklists
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_25900.yml b/changelogs/unreleased/issue_25900.yml
deleted file mode 100644
index b4b72b8a20c..00000000000
--- a/changelogs/unreleased/issue_25900.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Gather issuable metadata to avoid n+1 queries on index view
-merge_request:
-author:
diff --git a/changelogs/unreleased/issue_26701.yml b/changelogs/unreleased/issue_26701.yml
deleted file mode 100644
index 6834351bf43..00000000000
--- a/changelogs/unreleased/issue_26701.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove JIRA closed status icon
-merge_request:
-author:
diff --git a/changelogs/unreleased/list_issues_with_no_labels.yml b/changelogs/unreleased/list_issues_with_no_labels.yml
deleted file mode 100644
index ab44841631b..00000000000
--- a/changelogs/unreleased/list_issues_with_no_labels.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Document ability to list issues with no labels using API
-merge_request: 9697
-author: Vignesh Ravichandran
diff --git a/changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml b/changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml
deleted file mode 100644
index bd5db5ac7af..00000000000
--- a/changelogs/unreleased/lnovy-gitlab-ce-empty-variables.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'UI: Allow a project variable to be set to an empty value'
-merge_request: 6044
-author: Lukáš Nový
diff --git a/changelogs/unreleased/long-file-name-overflow.yml b/changelogs/unreleased/long-file-name-overflow.yml
deleted file mode 100644
index 7ccf05491e1..00000000000
--- a/changelogs/unreleased/long-file-name-overflow.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed long file names overflowing under action buttons
-merge_request:
-author:
diff --git a/changelogs/unreleased/migrate-pipeline-events-and-email-service.yml b/changelogs/unreleased/migrate-pipeline-events-and-email-service.yml
deleted file mode 100644
index ce4d5092c17..00000000000
--- a/changelogs/unreleased/migrate-pipeline-events-and-email-service.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Migrate SlackService and MattermostService from build_events to pipeline_events, and migrate BuildsEmailService to PipelinesEmailService. Update Hipchat to use pipeline events rather than build events.
-merge_request: 8196
-author:
diff --git a/changelogs/unreleased/mock-ci-service.yml b/changelogs/unreleased/mock-ci-service.yml
deleted file mode 100644
index 24c6366177f..00000000000
--- a/changelogs/unreleased/mock-ci-service.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add Mock CI service/integration for development
-merge_request:
-author:
diff --git a/changelogs/unreleased/move_tags_service_to_namespace.yml b/changelogs/unreleased/move_tags_service_to_namespace.yml
deleted file mode 100644
index ba76f291162..00000000000
--- a/changelogs/unreleased/move_tags_service_to_namespace.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Move tag services to Tags namespace
-merge_request:
-author: dixpac
diff --git a/changelogs/unreleased/moving-issue-with-two-list-labels.yml b/changelogs/unreleased/moving-issue-with-two-list-labels.yml
deleted file mode 100644
index d5ea81e3810..00000000000
--- a/changelogs/unreleased/moving-issue-with-two-list-labels.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Removes label when moving issue to another list that it is currently in
-merge_request:
-author:
diff --git a/changelogs/unreleased/mr-diff-comment-button.yml b/changelogs/unreleased/mr-diff-comment-button.yml
deleted file mode 100644
index 1dc6ed1c495..00000000000
--- a/changelogs/unreleased/mr-diff-comment-button.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Improved diff comment button UX
-merge_request:
-author:
diff --git a/changelogs/unreleased/new-branch-fixture.yml b/changelogs/unreleased/new-branch-fixture.yml
deleted file mode 100644
index ce5ed816102..00000000000
--- a/changelogs/unreleased/new-branch-fixture.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace static fixture for new_branch_spec.js
-merge_request: 9131
-author: winniehell
diff --git a/changelogs/unreleased/only-create-unmergeable-todo-once.yml b/changelogs/unreleased/only-create-unmergeable-todo-once.yml
deleted file mode 100644
index e675ed945ad..00000000000
--- a/changelogs/unreleased/only-create-unmergeable-todo-once.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Only create unmergeable todos once when MR fails to merge
-merge_request:
-author:
diff --git a/changelogs/unreleased/only-yield-valid-reference-matches.yml b/changelogs/unreleased/only-yield-valid-reference-matches.yml
deleted file mode 100644
index 95da3cc56fd..00000000000
--- a/changelogs/unreleased/only-yield-valid-reference-matches.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Only yield valid references in ReferenceFilter.references_in
-merge_request:
-author:
diff --git a/changelogs/unreleased/pages-0-4-0.yml b/changelogs/unreleased/pages-0-4-0.yml
deleted file mode 100644
index 7286b25125e..00000000000
--- a/changelogs/unreleased/pages-0-4-0.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use GitLab Pages v0.4.0
-merge_request: 9896
-author:
diff --git a/changelogs/unreleased/paginate-all-the-things.yml b/changelogs/unreleased/paginate-all-the-things.yml
deleted file mode 100644
index 52f23ba52a9..00000000000
--- a/changelogs/unreleased/paginate-all-the-things.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Paginate all endpoints that return an array'
-merge_request: 8606
-author: Robert Schilling
diff --git a/changelogs/unreleased/pass in current_user in MergeRequest and MergeRequestsHelper.yml b/changelogs/unreleased/pass in current_user in MergeRequest and MergeRequestsHelper.yml
deleted file mode 100644
index 0751047c3c0..00000000000
--- a/changelogs/unreleased/pass in current_user in MergeRequest and MergeRequestsHelper.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: pass in current_user in MergeRequest and MergeRequestsHelper
-merge_request: 8624
-author: Dongqing Hu
diff --git a/changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml b/changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml
deleted file mode 100644
index 74e0c18fa67..00000000000
--- a/changelogs/unreleased/pass_coverage_value_to_commit_status_api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make it possible to pass coverage value to commit status API
-merge_request: 9214
-author: wendy0402
diff --git a/changelogs/unreleased/pipeline-blocking-actions.yml b/changelogs/unreleased/pipeline-blocking-actions.yml
deleted file mode 100644
index 6bde501de18..00000000000
--- a/changelogs/unreleased/pipeline-blocking-actions.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Make it possible to configure blocking manual actions
-merge_request: 9585
-author:
diff --git a/changelogs/unreleased/pipelines-build-tooltip.yml b/changelogs/unreleased/pipelines-build-tooltip.yml
new file mode 100644
index 00000000000..000276e1de3
--- /dev/null
+++ b/changelogs/unreleased/pipelines-build-tooltip.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed job tooltip being cut-off
+merge_request:
+author:
diff --git a/changelogs/unreleased/priority-to-label-priority.yml b/changelogs/unreleased/priority-to-label-priority.yml
deleted file mode 100644
index 2d9c58bfd9b..00000000000
--- a/changelogs/unreleased/priority-to-label-priority.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename priority sorting option to label priority
-merge_request:
-author:
diff --git a/changelogs/unreleased/projects-list-line-breaks.yml b/changelogs/unreleased/projects-list-line-breaks.yml
new file mode 100644
index 00000000000..179d7081293
--- /dev/null
+++ b/changelogs/unreleased/projects-list-line-breaks.yml
@@ -0,0 +1,4 @@
+---
+title: Fixed projects list lines breaking
+merge_request:
+author:
diff --git a/changelogs/unreleased/protected-branch-dropdown-titles.yml b/changelogs/unreleased/protected-branch-dropdown-titles.yml
deleted file mode 100644
index df82cc00fc9..00000000000
--- a/changelogs/unreleased/protected-branch-dropdown-titles.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added headers to protected branch access dropdowns
-merge_request:
-author:
diff --git a/changelogs/unreleased/quick-submit-fixture.yml b/changelogs/unreleased/quick-submit-fixture.yml
deleted file mode 100644
index a2cf05dabec..00000000000
--- a/changelogs/unreleased/quick-submit-fixture.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace static fixture for behaviors/quick_submit_spec.js
-merge_request: 9086
-author: winniehell
diff --git a/changelogs/unreleased/remember-me-missasligned-mobile.yml b/changelogs/unreleased/remember-me-missasligned-mobile.yml
new file mode 100644
index 00000000000..7071d32727f
--- /dev/null
+++ b/changelogs/unreleased/remember-me-missasligned-mobile.yml
@@ -0,0 +1,4 @@
+---
+title: Corrected alignment for the remember-me checkbox in the login view
+merge_request:
+author:
diff --git a/changelogs/unreleased/removal_of_unused_parameter.yml b/changelogs/unreleased/removal_of_unused_parameter.yml
deleted file mode 100644
index 26bffafd9d9..00000000000
--- a/changelogs/unreleased/removal_of_unused_parameter.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'removed unused parameter ''status_only: true'''
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove-es6-extension.yml b/changelogs/unreleased/remove-es6-extension.yml
deleted file mode 100644
index 65f4a7a7867..00000000000
--- a/changelogs/unreleased/remove-es6-extension.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove es6 file extension from JavaScript files
-merge_request: 9241
-author: winniehell
diff --git a/changelogs/unreleased/remove-inactive-default-email-services.yml b/changelogs/unreleased/remove-inactive-default-email-services.yml
deleted file mode 100644
index c32c1390e4e..00000000000
--- a/changelogs/unreleased/remove-inactive-default-email-services.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove inactive default email services
-merge_request: 8987
-author:
diff --git a/changelogs/unreleased/remove-jquery-ui-datepicker.yml b/changelogs/unreleased/remove-jquery-ui-datepicker.yml
deleted file mode 100644
index cd00690d774..00000000000
--- a/changelogs/unreleased/remove-jquery-ui-datepicker.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replaced jQuery UI datepicker
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove-jquery-ui-plugins.yml b/changelogs/unreleased/remove-jquery-ui-plugins.yml
deleted file mode 100644
index c768f702ba2..00000000000
--- a/changelogs/unreleased/remove-jquery-ui-plugins.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Removed jQuery UI highlight & autocomplete
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove-jquery-ui-sortable.yml b/changelogs/unreleased/remove-jquery-ui-sortable.yml
deleted file mode 100644
index 35f47822738..00000000000
--- a/changelogs/unreleased/remove-jquery-ui-sortable.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replaced jQuery UI sortable
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove-new-relic-gem.yml b/changelogs/unreleased/remove-new-relic-gem.yml
deleted file mode 100644
index b15ecd3e4e7..00000000000
--- a/changelogs/unreleased/remove-new-relic-gem.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove the newrelic gem
-merge_request: 9622
-author: Robert Schilling
diff --git a/changelogs/unreleased/remove-readme-option.yml b/changelogs/unreleased/remove-readme-option.yml
deleted file mode 100644
index 1d4c862c00e..00000000000
--- a/changelogs/unreleased/remove-readme-option.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove readme-only project view preference
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove-subscribe-label-tooltip.yml b/changelogs/unreleased/remove-subscribe-label-tooltip.yml
deleted file mode 100644
index 90b71d3be51..00000000000
--- a/changelogs/unreleased/remove-subscribe-label-tooltip.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove tooltips from label subscription buttons
-merge_request:
-author:
diff --git a/changelogs/unreleased/remove-unused-ci-tables.yml b/changelogs/unreleased/remove-unused-ci-tables.yml
deleted file mode 100644
index fccfb882bd9..00000000000
--- a/changelogs/unreleased/remove-unused-ci-tables.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove various unused CI tables and columns
-merge_request: 9639
-author:
diff --git a/changelogs/unreleased/rename-ci_commits-to-ci_pipeline.yml b/changelogs/unreleased/rename-ci_commits-to-ci_pipeline.yml
deleted file mode 100644
index 4067b3de00c..00000000000
--- a/changelogs/unreleased/rename-ci_commits-to-ci_pipeline.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename table ci_commits to ci_pipelines
-merge_request: 9638
-author:
diff --git a/changelogs/unreleased/rename-retry-failed-pipeline-to-retry.yml b/changelogs/unreleased/rename-retry-failed-pipeline-to-retry.yml
deleted file mode 100644
index b813127b1e6..00000000000
--- a/changelogs/unreleased/rename-retry-failed-pipeline-to-retry.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename retry failed button on pipeline page to just retry
-merge_request:
-author:
diff --git a/changelogs/unreleased/rename_delete_services.yml b/changelogs/unreleased/rename_delete_services.yml
deleted file mode 100644
index 686a1ef3d55..00000000000
--- a/changelogs/unreleased/rename_delete_services.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix inconsistent naming for services that delete things
-merge_request: 5803
-author: dixpac
diff --git a/changelogs/unreleased/rename_files_delete_service.yml b/changelogs/unreleased/rename_files_delete_service.yml
deleted file mode 100644
index 4de1c5b0d63..00000000000
--- a/changelogs/unreleased/rename_files_delete_service.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename Files::DeleteService to Files::DestroyService
-merge_request: 9110
-author: dixpac
diff --git a/changelogs/unreleased/replace-npm-with-yarn.yml b/changelogs/unreleased/replace-npm-with-yarn.yml
deleted file mode 100644
index 5e795eb0c8d..00000000000
--- a/changelogs/unreleased/replace-npm-with-yarn.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: replace npm with yarn and add yarn.lock
-merge_request: 9055
-author:
diff --git a/changelogs/unreleased/replace_closing_mr_icon.yml b/changelogs/unreleased/replace_closing_mr_icon.yml
new file mode 100644
index 00000000000..4d7b5fa67a7
--- /dev/null
+++ b/changelogs/unreleased/replace_closing_mr_icon.yml
@@ -0,0 +1,4 @@
+---
+title: Replace closing MR icon
+merge_request: 10103
+author: blackst0ne
diff --git a/changelogs/unreleased/requires-input-fixture.yml b/changelogs/unreleased/requires-input-fixture.yml
deleted file mode 100644
index be674499429..00000000000
--- a/changelogs/unreleased/requires-input-fixture.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Replace static fixture for behaviors/requires_input_spec.js
-merge_request: 9162
-author: winniehell
diff --git a/changelogs/unreleased/rfr-20170307-change-default-project-number-limit.yml b/changelogs/unreleased/rfr-20170307-change-default-project-number-limit.yml
deleted file mode 100644
index e799dd3b48d..00000000000
--- a/changelogs/unreleased/rfr-20170307-change-default-project-number-limit.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Change project count limit from 10 to 100000
-merge_request:
-author:
diff --git a/changelogs/unreleased/routes-lower-case.yml b/changelogs/unreleased/routes-lower-case.yml
deleted file mode 100644
index 2110956680c..00000000000
--- a/changelogs/unreleased/routes-lower-case.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove repeated routes.path check for postgresql database
-merge_request:
-author: mhasbini
diff --git a/changelogs/unreleased/rss-btn-alignment-fix.yml b/changelogs/unreleased/rss-btn-alignment-fix.yml
deleted file mode 100644
index c8f57ec0b7c..00000000000
--- a/changelogs/unreleased/rss-btn-alignment-fix.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fixed RSS button alignment on activity pages
-merge_request:
-author:
diff --git a/changelogs/unreleased/seed-abuse-reports.yml b/changelogs/unreleased/seed-abuse-reports.yml
deleted file mode 100644
index 6fbcb81ae3f..00000000000
--- a/changelogs/unreleased/seed-abuse-reports.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Seed abuse reports for development
-merge_request:
-author:
diff --git a/changelogs/unreleased/set-default-cache-key-for-jobs.yml b/changelogs/unreleased/set-default-cache-key-for-jobs.yml
deleted file mode 100644
index b69348d2ece..00000000000
--- a/changelogs/unreleased/set-default-cache-key-for-jobs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Set default cache key to "default" for jobs
-merge_request: 9666
-author:
diff --git a/changelogs/unreleased/settings-tab.yml b/changelogs/unreleased/settings-tab.yml
deleted file mode 100644
index 69990c9a917..00000000000
--- a/changelogs/unreleased/settings-tab.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Moved project settings from the gear drop-down menu to a tab
-merge_request: 9786
-author:
diff --git a/changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml b/changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml
deleted file mode 100644
index 57f1474093a..00000000000
--- a/changelogs/unreleased/sh-bump-hashie-to-3-5-5.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Bump Hashie to 3.5.5 and omniauth to 1.4.2 to eliminate warning noise
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-delete-user-permission-check.yml b/changelogs/unreleased/sh-delete-user-permission-check.yml
deleted file mode 100644
index c0e79aae2a8..00000000000
--- a/changelogs/unreleased/sh-delete-user-permission-check.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add user deletion permission check in `Users::DestroyService`
-merge_request:
-author:
diff --git a/changelogs/unreleased/sh-remove-tags-from-explore.yml b/changelogs/unreleased/sh-remove-tags-from-explore.yml
new file mode 100644
index 00000000000..b76ec89a006
--- /dev/null
+++ b/changelogs/unreleased/sh-remove-tags-from-explore.yml
@@ -0,0 +1,4 @@
+---
+title: Remove Tags filter from Projects Explore dropdown
+merge_request:
+author:
diff --git a/changelogs/unreleased/slow-search-changelog.yml b/changelogs/unreleased/slow-search-changelog.yml
new file mode 100644
index 00000000000..d50cf1f94cd
--- /dev/null
+++ b/changelogs/unreleased/slow-search-changelog.yml
@@ -0,0 +1,4 @@
+---
+title: Simplify search queries for projects and merge requests
+merge_request: 10053
+author: mhasbini
diff --git a/changelogs/unreleased/snippets-search.yml b/changelogs/unreleased/snippets-search.yml
deleted file mode 100644
index 00cf34f4a48..00000000000
--- a/changelogs/unreleased/snippets-search.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix snippets search result spacing
-merge_request:
-author:
diff --git a/changelogs/unreleased/sort-builds-in-stage-dropdown.yml b/changelogs/unreleased/sort-builds-in-stage-dropdown.yml
deleted file mode 100644
index 646f25125b1..00000000000
--- a/changelogs/unreleased/sort-builds-in-stage-dropdown.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Sort builds in stage dropdown
-merge_request:
-author:
diff --git a/changelogs/unreleased/ssh-key-paste.yml b/changelogs/unreleased/ssh-key-paste.yml
deleted file mode 100644
index 1e34ef60f6e..00000000000
--- a/changelogs/unreleased/ssh-key-paste.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: SSH key field updates title after pasting key
-merge_request:
-author:
diff --git a/changelogs/unreleased/ssrf-protections.yml b/changelogs/unreleased/ssrf-protections.yml
deleted file mode 100644
index 8d803738009..00000000000
--- a/changelogs/unreleased/ssrf-protections.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: To protect against Server-side Request Forgery project import URLs are now prohibited against localhost or the server IP except for the assigned instance URL and port. Imports are also prohibited from ports below 1024 with the exception of ports 22, 80, and 443.
-merge_request:
-author:
diff --git a/changelogs/unreleased/static-navbar.yml b/changelogs/unreleased/static-navbar.yml
deleted file mode 100644
index eaf478a48d0..00000000000
--- a/changelogs/unreleased/static-navbar.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Remove fixed positioning from top nav
-merge_request: !7547
-author:
diff --git a/changelogs/unreleased/task_list_refactor.yml b/changelogs/unreleased/task_list_refactor.yml
deleted file mode 100644
index 68942dadaa8..00000000000
--- a/changelogs/unreleased/task_list_refactor.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Deduplicate markdown task lists
-merge_request:
-author:
diff --git a/changelogs/unreleased/tc-api-pipeline-jobs.yml b/changelogs/unreleased/tc-api-pipeline-jobs.yml
deleted file mode 100644
index 993c1b6526a..00000000000
--- a/changelogs/unreleased/tc-api-pipeline-jobs.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Add GET /projects/:id/pipelines/:pipeline_id/jobs endpoint
-merge_request: 9727
-author:
diff --git a/changelogs/unreleased/tc-fix-project-create-500.yml b/changelogs/unreleased/tc-fix-project-create-500.yml
deleted file mode 100644
index 1b746a41eab..00000000000
--- a/changelogs/unreleased/tc-fix-project-create-500.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Fix for creating a project through API when import_url is nil
-merge_request: 9841
-author:
diff --git a/changelogs/unreleased/tc-pipeline-show-trigger-date.yml b/changelogs/unreleased/tc-pipeline-show-trigger-date.yml
new file mode 100644
index 00000000000..4de784d98f3
--- /dev/null
+++ b/changelogs/unreleased/tc-pipeline-show-trigger-date.yml
@@ -0,0 +1,4 @@
+---
+title: Show correct user & creation time in heading of the pipeline page
+merge_request: 9936
+author:
diff --git a/changelogs/unreleased/time-tracking-color-not-consistent.yml b/changelogs/unreleased/time-tracking-color-not-consistent.yml
new file mode 100644
index 00000000000..50ec9efb1ff
--- /dev/null
+++ b/changelogs/unreleased/time-tracking-color-not-consistent.yml
@@ -0,0 +1,4 @@
+---
+title: Corrected time tracking icon color in the issuable side bar
+merge_request:
+author:
diff --git a/changelogs/unreleased/unified-member-api-response.yml b/changelogs/unreleased/unified-member-api-response.yml
deleted file mode 100644
index 0a60b4d46a3..00000000000
--- a/changelogs/unreleased/unified-member-api-response.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: 'API: Return 400 for all validation erros in the mebers API'
-merge_request: 9523
-author: Robert Schilling
diff --git a/changelogs/unreleased/update-ace.yml b/changelogs/unreleased/update-ace.yml
deleted file mode 100644
index dbe476e3ae0..00000000000
--- a/changelogs/unreleased/update-ace.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Update code editor (ACE) to 1.2.6, to fix input problems with compose key
-merge_request:
-author:
diff --git a/changelogs/unreleased/update-vue-2-1.yml b/changelogs/unreleased/update-vue-2-1.yml
deleted file mode 100644
index acc42bf00b1..00000000000
--- a/changelogs/unreleased/update-vue-2-1.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: update Vue to v2.1.10
-merge_request: 9386
-author:
diff --git a/changelogs/unreleased/use-redis-channel-to-post-runner-notifcations.yml b/changelogs/unreleased/use-redis-channel-to-post-runner-notifcations.yml
deleted file mode 100644
index ff5a58f6232..00000000000
--- a/changelogs/unreleased/use-redis-channel-to-post-runner-notifcations.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use redis channel to post notifications
-merge_request:
-author:
diff --git a/changelogs/unreleased/user-calendar-border.yml b/changelogs/unreleased/user-calendar-border.yml
deleted file mode 100644
index 8ebcca83256..00000000000
--- a/changelogs/unreleased/user-calendar-border.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Removed top border from user contribution calendar
-merge_request:
-author:
diff --git a/changelogs/unreleased/user-callout-showing-on-all-profiles.yml b/changelogs/unreleased/user-callout-showing-on-all-profiles.yml
new file mode 100644
index 00000000000..b8eb5a149b7
--- /dev/null
+++ b/changelogs/unreleased/user-callout-showing-on-all-profiles.yml
@@ -0,0 +1,4 @@
+---
+title: User callout only shows on current users profile
+merge_request:
+author:
diff --git a/changelogs/unreleased/user-callouts.yml b/changelogs/unreleased/user-callouts.yml
deleted file mode 100644
index f6ce06a3d8f..00000000000
--- a/changelogs/unreleased/user-callouts.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Added user callouts to the projects dashboard and user profile
-merge_request:
-author:
diff --git a/changelogs/unreleased/user-profile-join-date.yml b/changelogs/unreleased/user-profile-join-date.yml
new file mode 100644
index 00000000000..f9d78b0dc3e
--- /dev/null
+++ b/changelogs/unreleased/user-profile-join-date.yml
@@ -0,0 +1,4 @@
+---
+title: Removed the hours & minutes from the users start date on their profile
+merge_request:
+author:
diff --git a/changelogs/unreleased/workhorse-1-4-0.yml b/changelogs/unreleased/workhorse-1-4-0.yml
deleted file mode 100644
index b55fabddb0f..00000000000
--- a/changelogs/unreleased/workhorse-1-4-0.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Use gitlab-workhorse 1.4.0
-merge_request: 9724
-author:
diff --git a/changelogs/unreleased/zj-builds-to-jobs-api.yml b/changelogs/unreleased/zj-builds-to-jobs-api.yml
deleted file mode 100644
index 473dd9bc8ed..00000000000
--- a/changelogs/unreleased/zj-builds-to-jobs-api.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename builds to job for the v4 API
-merge_request: 9463
-author:
diff --git a/changelogs/unreleased/zj-chat-notification-default-branch.yml b/changelogs/unreleased/zj-chat-notification-default-branch.yml
new file mode 100644
index 00000000000..fa0052d5034
--- /dev/null
+++ b/changelogs/unreleased/zj-chat-notification-default-branch.yml
@@ -0,0 +1,4 @@
+---
+title: Only send chat notifications for the default branch
+merge_request:
+author:
diff --git a/changelogs/unreleased/zj-variables-build-job.yml b/changelogs/unreleased/zj-variables-build-job.yml
deleted file mode 100644
index 1cb0919f824..00000000000
--- a/changelogs/unreleased/zj-variables-build-job.yml
+++ /dev/null
@@ -1,4 +0,0 @@
----
-title: Rename job environment variables to new terminology
-merge_request: 9756
-author:
diff --git a/config/environments/test.rb b/config/environments/test.rb
index fb25d3a8b14..a25c5016a3b 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,4 +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')
+
# Settings specified here will take precedence over those in config/application.rb
# The test environment is used exclusively to run your application's
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index ba7f6773985..3747baf4c3b 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -450,7 +450,7 @@ production: &base
# This setting is obsolete because we expect it to be moved under
# repositories/storages in GitLab 9.1.
#
- # socket_path: tmp/sockets/gitaly.socket
+ # socket_path: tmp/sockets/private/gitaly.socket
#
# 4. Advanced settings
diff --git a/config/webpack.config.js b/config/webpack.config.js
index c6794d6b944..0859c8416c8 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -18,10 +18,10 @@ var config = {
context: path.join(ROOT_PATH, 'app/assets/javascripts'),
entry: {
common: './commons/index.js',
- common_vue: ['vue', 'vue-resource'],
+ common_vue: ['vue', './vue_shared/common_vue.js'],
common_d3: ['d3'],
main: './main.js',
- blob_edit: './blob_edit/blob_edit_bundle.js',
+ blob: './blob_edit/blob_bundle.js',
boards: './boards/boards_bundle.js',
simulate_drag: './test_utils/simulate_drag.js',
cycle_analytics: './cycle_analytics/cycle_analytics_bundle.js',
@@ -132,7 +132,7 @@ var config = {
'empty_states': path.join(ROOT_PATH, 'app/views/shared/empty_states'),
'icons': path.join(ROOT_PATH, 'app/views/shared/icons'),
'vendor': path.join(ROOT_PATH, 'vendor/assets/javascripts'),
- 'vue$': 'vue/dist/vue.common.js',
+ 'vue$': 'vue/dist/vue.esm.js',
}
}
}
diff --git a/db/migrate/20170317203554_index_routes_path_for_like.rb b/db/migrate/20170317203554_index_routes_path_for_like.rb
new file mode 100644
index 00000000000..7ac09b2abe5
--- /dev/null
+++ b/db/migrate/20170317203554_index_routes_path_for_like.rb
@@ -0,0 +1,29 @@
+# See http://doc.gitlab.com/ce/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class IndexRoutesPathForLike < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ INDEX_NAME = 'index_routes_on_path_text_pattern_ops'
+
+ disable_ddl_transaction!
+
+ def up
+ return unless Gitlab::Database.postgresql?
+
+ unless index_exists?(:routes, :path, name: INDEX_NAME)
+ execute("CREATE INDEX CONCURRENTLY #{INDEX_NAME} ON routes (path varchar_pattern_ops);")
+ end
+ end
+
+ def down
+ return unless Gitlab::Database.postgresql?
+
+ if index_exists?(:routes, :path, name: INDEX_NAME)
+ execute("DROP INDEX CONCURRENTLY #{INDEX_NAME};")
+ end
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 5d9b219ebea..8f32b62bf14 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -61,6 +61,7 @@ ActiveRecord::Schema.define(version: 20170322013926) do
t.boolean "shared_runners_enabled", default: true, null: false
t.integer "max_artifacts_size", default: 100, null: false
t.string "runners_registration_token"
+ t.integer "max_pages_size", default: 100, null: false
t.boolean "require_two_factor_authentication", default: false
t.integer "two_factor_grace_period", default: 48
t.boolean "metrics_enabled", default: false
@@ -110,7 +111,6 @@ ActiveRecord::Schema.define(version: 20170322013926) do
t.string "plantuml_url"
t.boolean "plantuml_enabled"
t.integer "terminal_max_session_time", default: 0, null: false
- t.integer "max_pages_size", default: 100, null: false
t.string "default_artifacts_expire_in", default: "0", null: false
t.integer "unique_ips_limit_per_user"
t.integer "unique_ips_limit_time_window"
@@ -693,8 +693,8 @@ ActiveRecord::Schema.define(version: 20170322013926) do
t.integer "visibility_level", default: 20, null: false
t.boolean "request_access_enabled", default: false, null: false
t.datetime "deleted_at"
- t.boolean "lfs_enabled"
t.text "description_html"
+ t.boolean "lfs_enabled"
t.integer "parent_id"
end
@@ -990,6 +990,7 @@ ActiveRecord::Schema.define(version: 20170322013926) do
end
add_index "routes", ["path"], name: "index_routes_on_path", unique: true, using: :btree
+ add_index "routes", ["path"], name: "index_routes_on_path_text_pattern_ops", using: :btree, opclasses: {"path"=>"varchar_pattern_ops"}
add_index "routes", ["source_type", "source_id"], name: "index_routes_on_source_type_and_source_id", unique: true, using: :btree
create_table "sent_notifications", force: :cascade do |t|
@@ -1236,8 +1237,8 @@ ActiveRecord::Schema.define(version: 20170322013926) do
t.datetime "otp_grace_period_started_at"
t.boolean "ldap_email", default: false, null: false
t.boolean "external", default: false
- t.string "organization"
t.string "incoming_email_token"
+ t.string "organization"
t.boolean "authorized_projects_populated"
t.boolean "ghost"
end
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
new file mode 100644
index 00000000000..30a4c08508d
--- /dev/null
+++ b/doc/administration/gitaly/index.md
@@ -0,0 +1,86 @@
+# Gitaly
+
+[Gitaly](https://gitlab.com/gitlab-org/gitlay) (introduced in GitLab
+9.0) is a service that provides high-level RPC access to Git
+repositories. As of GitLab 9.0 it is still an optional component with
+limited scope.
+
+GitLab components that access Git repositories (gitlab-rails,
+gitlab-shell, gitlab-workhorse) act as clients to Gitaly. End users do
+not have direct access to Gitaly.
+
+## Configuring Gitaly
+
+The Gitaly service itself is configured via environment variables.
+These variables are documented [in the gitaly
+repository](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/configuration/README.md).
+
+To change a Gitaly environment variable in Omnibus you can use
+`gitaly['env']` in `/etc/gitlab/gitlab.rb`. Changes will be applied
+when you run `gitlab-ctl reconfigure`.
+
+```ruby
+gitaly['env'] = {
+ 'GITALY_MY_VARIABLE' => 'value'
+}
+```
+
+To change a Gitaly environment variable in installations from source
+you can edit `/home/git/gitaly/env`.
+
+```shell
+GITALY_MY_VARIABLE='value'
+```
+
+Changes to `/home/git/gitaly/env` are applied when you run `service
+gitlab restart`.
+
+## Configuring GitLab to not use Gitaly
+
+Gitaly is still an optional component in GitLab 9.0. This means you
+can choose to not use it.
+
+In Omnibus you can make the following change in
+`/etc/gitlab/gitlab.rb` and reconfigure. This will both disable the
+Gitaly service and configure the rest of GitLab not to use it.
+
+```ruby
+gitaly['enable'] = false
+```
+
+In source installations, edit `/home/git/gitlab/config/gitlab.yml` and
+make sure `socket_path` in the `gitaly` section is commented out. This
+does not disable the Gitaly service; it only prevents it from being
+used.
+
+Apply the change with `service gitlab restart`.
+
+```yaml
+ gitaly:
+ # socket_path: tmp/sockets/private/gitlay.socket
+```
+
+## Disabling or enabling the Gitaly service
+
+Be careful: if you disable Gitaly without instructing the rest of your
+GitLab installation not to use Gitaly, you may end up with errors
+because GitLab tries to access a service that is not running.
+
+To disable the Gitaly service in your Omnibus installation, add the
+following line to `/etc/gitlab/gitlab.rb`:
+
+```ruby
+gitaly['enable'] = false
+```
+
+When you run `gitlab-ctl reconfigure` the Gitaly service will be
+disabled.
+
+To disable the Gitaly service in an installation from source, add the
+following to `/etc/default/gitlab`:
+
+```shell
+gitaly_enabled=false
+```
+
+When you run `service gitlab restart` Gitaly will be disabled. \ No newline at end of file
diff --git a/doc/administration/high_availability/database.md b/doc/administration/high_availability/database.md
index 0a08591c3ce..cf3aca106e9 100644
--- a/doc/administration/high_availability/database.md
+++ b/doc/administration/high_availability/database.md
@@ -13,6 +13,8 @@ Database Service (RDS) that runs PostgreSQL.
If you use a cloud-managed service, or provide your own PostgreSQL:
+1. Setup PostgreSQL according to the
+ [database requirements document](doc/install/requirements.md#database).
1. Set up a `gitlab` username with a password of your choice. The `gitlab` user
needs privileges to create the `gitlabhq_production` database.
1. Configure the GitLab application servers with the appropriate details.
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index 3893d837006..bf1aa6b9ac5 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -26,7 +26,7 @@ options:
circumstances it could lead to data loss if a failure occurs before data has
synced.
-## Client mount options
+## NFS Client mount options
Below is an example of an NFS mount point we use on GitLab.com:
diff --git a/doc/api/README.md b/doc/api/README.md
index 58d090b8f5e..e627b6f2ee8 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -74,6 +74,12 @@ returned with status code `401`:
}
```
+### Session Cookie
+
+When signing in to GitLab as an ordinary user, a `_gitlab_session` cookie is
+set. The API will use this cookie for authentication if it is present, but using
+the API to generate a new session cookie is currently not supported.
+
### Private Tokens
You need to pass a `private_token` parameter via query string or header. If passed as a
@@ -113,65 +119,25 @@ moment – `read_user` and `api` – the groundwork has been laid to add more sc
At any time you can revoke any personal access token by just clicking **Revoke**.
-### Session Cookie
+### Impersonation tokens
-When signing in to GitLab as an ordinary user, a `_gitlab_session` cookie is
-set. The API will use this cookie for authentication if it is present, but using
-the API to generate a new session cookie is currently not supported.
+> [Introduced][ce-9099] in GitLab 9.0. Needs admin permissions.
-## Basic Usage
+Impersonation tokens are a type of [Personal Access Token](#personal-access-tokens)
+that can only be created by an admin for a specific user.
-API requests should be prefixed with `api` and the API version. The API version
-is defined in [`lib/api.rb`][lib-api-url].
+They are a better alternative to using the user's password/private token
+or using the [Sudo](#sudo) feature which also requires the admin's password
+or private token, since the password/token can change over time. Impersonation
+tokens are a great fit if you want to build applications or tools which
+authenticate with the API as a specific user.
-Example of a valid API request:
+For more information about the usage please refer to the
+[users API](users.md#retrieve-user-impersonation-tokens) docs.
-```shell
-GET https://gitlab.example.com/api/v4/projects?private_token=9koXpg98eAheJpvBs5tK
-```
-
-Example of a valid API request using cURL and authentication via header:
-
-```shell
-curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects"
-```
-
-The API uses JSON to serialize data. You don't need to specify `.json` at the
-end of an API URL.
-
-## Status codes
-
-The API is designed to return different status codes according to context and
-action. This way, if a request results in an error, the caller is able to get
-insight into what went wrong.
-
-The following table gives an overview of how the API functions generally behave.
-
-| Request type | Description |
-| ------------ | ----------- |
-| `GET` | Access one or more resources and return the result as JSON. |
-| `POST` | Return `201 Created` if the resource is successfully created and return the newly created resource as JSON. |
-| `GET` / `PUT` / `DELETE` | Return `200 OK` if the resource is accessed, modified or deleted successfully. The (modified) result is returned as JSON. |
-| `DELETE` | Designed to be idempotent, meaning a request to a resource still returns `200 OK` even it was deleted before or is not available. The reasoning behind this, is that the user is not really interested if the resource existed before or not. |
-
-The following table shows the possible return codes for API requests.
-
-| Return values | Description |
-| ------------- | ----------- |
-| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. |
-| `204 No Content` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
-| `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
-| `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
-| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
-| `401 Unauthorized` | The user is not authenticated, a valid [user token](#authentication) is necessary. |
-| `403 Forbidden` | The request is not allowed, e.g., the user is not allowed to delete a project. |
-| `404 Not Found` | A resource could not be accessed, e.g., an ID for a resource could not be found. |
-| `405 Method Not Allowed` | The request is not supported. |
-| `409 Conflict` | A conflicting resource already exists, e.g., creating a project with a name that already exists. |
-| `422 Unprocessable` | The entity could not be processed. |
-| `500 Server Error` | While handling the request something went wrong server-side. |
+### Sudo
-## Sudo
+> Needs admin permissions.
All API requests support performing an API call as if you were another user,
provided your private token is from an administrator account. You need to pass
@@ -202,7 +168,7 @@ returned with status code `404`:
Example of a valid API call and a request using cURL with sudo request,
providing a username:
-```shell
+```
GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=username
```
@@ -213,7 +179,7 @@ curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: username" "h
Example of a valid API call and a request using cURL with sudo request,
providing an ID:
-```shell
+```
GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=23
```
@@ -221,13 +187,57 @@ GET /projects?private_token=9koXpg98eAheJpvBs5tK&sudo=23
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --header "SUDO: 23" "https://gitlab.example.com/api/v4/projects"
```
-## Impersonation Tokens
+## Basic Usage
+
+API requests should be prefixed with `api` and the API version. The API version
+is defined in [`lib/api.rb`][lib-api-url].
+
+Example of a valid API request:
+
+```
+GET /projects?private_token=9koXpg98eAheJpvBs5tK
+```
+
+Example of a valid API request using cURL and authentication via header:
+
+```shell
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects"
+```
+
+The API uses JSON to serialize data. You don't need to specify `.json` at the
+end of an API URL.
+
+## Status codes
+
+The API is designed to return different status codes according to context and
+action. This way, if a request results in an error, the caller is able to get
+insight into what went wrong.
+
+The following table gives an overview of how the API functions generally behave.
+
+| Request type | Description |
+| ------------ | ----------- |
+| `GET` | Access one or more resources and return the result as JSON. |
+| `POST` | Return `201 Created` if the resource is successfully created and return the newly created resource as JSON. |
+| `GET` / `PUT` / `DELETE` | Return `200 OK` if the resource is accessed, modified or deleted successfully. The (modified) result is returned as JSON. |
+| `DELETE` | Designed to be idempotent, meaning a request to a resource still returns `200 OK` even it was deleted before or is not available. The reasoning behind this, is that the user is not really interested if the resource existed before or not. |
-Impersonation Tokens are a type of Personal Access Token that can only be created by an admin for a specific user. These can be used by automated tools
-to authenticate with the API as a specific user, as a better alternative to using the user's password or private token directly, which may change over time,
-and to using the [Sudo](#sudo) feature, which requires the tool to know an admin's password or private token, which can change over time as well and are extremely powerful.
+The following table shows the possible return codes for API requests.
-For more information about the usage please refer to the [Users](users.md) page
+| Return values | Description |
+| ------------- | ----------- |
+| `200 OK` | The `GET`, `PUT` or `DELETE` request was successful, the resource(s) itself is returned as JSON. |
+| `204 No Content` | The server has successfully fulfilled the request and that there is no additional content to send in the response payload body. |
+| `201 Created` | The `POST` request was successful and the resource is returned as JSON. |
+| `304 Not Modified` | Indicates that the resource has not been modified since the last request. |
+| `400 Bad Request` | A required attribute of the API request is missing, e.g., the title of an issue is not given. |
+| `401 Unauthorized` | The user is not authenticated, a valid [user token](#authentication) is necessary. |
+| `403 Forbidden` | The request is not allowed, e.g., the user is not allowed to delete a project. |
+| `404 Not Found` | A resource could not be accessed, e.g., an ID for a resource could not be found. |
+| `405 Method Not Allowed` | The request is not supported. |
+| `409 Conflict` | A conflicting resource already exists, e.g., creating a project with a name that already exists. |
+| `422 Unprocessable` | The entity could not be processed. |
+| `500 Server Error` | While handling the request something went wrong server-side. |
## Pagination
@@ -307,14 +317,14 @@ For example, an issue might have `id: 46` and `iid: 5`.
That means that if you want to get an issue via the API you should use the `id`:
-```bash
+```
GET /projects/42/issues/:id
```
On the other hand, if you want to create a link to a web page you should use
the `iid`:
-```bash
+```
GET /projects/42/issues/:iid
```
@@ -398,3 +408,4 @@ programming languages. Visit the [GitLab website] for a complete list.
[lib-api-url]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/api/api.rb
[ce-3749]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3749
[ce-5951]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5951
+[ce-9099]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9099
diff --git a/doc/api/pipeline_triggers.md b/doc/api/pipeline_triggers.md
index fdb41a1d615..50fc19f0e08 100644
--- a/doc/api/pipeline_triggers.md
+++ b/doc/api/pipeline_triggers.md
@@ -41,10 +41,10 @@ Get details of project's build trigger.
GET /projects/:id/triggers/:trigger_id
```
-| Attribute | Type | required | Description |
-|-----------|---------|----------|--------------------------|
-| `id` | integer | yes | The ID of a project |
-| `token` | string | yes | The `token` of a trigger |
+| Attribute | Type | required | Description |
+|--------------|---------|----------|--------------------------|
+| `id` | integer | yes | The ID of a project |
+| `trigger_id` | integer | yes | The trigger id |
```
curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/projects/1/triggers/5"
@@ -103,6 +103,7 @@ PUT /projects/:id/triggers/:trigger_id
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
+| `id` | integer | yes | The ID of a project |
| `trigger_id` | integer | yes | The trigger id |
| `description` | string | no | The trigger name |
@@ -133,6 +134,7 @@ POST /projects/:id/triggers/:trigger_id/take_ownership
| Attribute | Type | required | Description |
|---------------|---------|----------|--------------------------|
+| `id` | integer | yes | The ID of a project |
| `trigger_id` | integer | yes | The trigger id |
```
diff --git a/doc/api/users.md b/doc/api/users.md
index 14b5c6c713e..2ada4d09c84 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -828,10 +828,12 @@ Example response:
]
```
-## Retrieve user impersonation tokens
+## Get all impersonation tokens of a user
-It retrieves every impersonation token of the user. Note that only administrators can do this.
-This function takes pagination parameters `page` and `per_page` to restrict the list of impersonation tokens.
+> Requires admin permissions.
+
+It retrieves every impersonation token of the user. Use the pagination
+parameters `page` and `per_page` to restrict the list of impersonation tokens.
```
GET /users/:user_id/impersonation_tokens
@@ -842,27 +844,50 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user_id` | integer | yes | The ID of the user |
-| `state` | string | no | filter tokens based on state (all, active, inactive) |
+| `state` | string | no | filter tokens based on state (`all`, `active`, `inactive`) |
+
+```
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/impersonation_tokens
+```
Example response:
+
```json
[
- {
- "id": 1,
- "name": "mytoken",
- "revoked": false,
- "expires_at": "2017-01-04",
- "scopes": ['api'],
- "active": true,
- "impersonation": true,
- "token": "9koXpg98eAheJpvBs5tK"
- }
+ {
+ "active" : true,
+ "token" : "EsMo-vhKfXGwX9RKrwiy",
+ "scopes" : [
+ "api"
+ ],
+ "revoked" : false,
+ "name" : "mytoken",
+ "id" : 2,
+ "created_at" : "2017-03-17T17:18:09.283Z",
+ "impersonation" : true,
+ "expires_at" : "2017-04-04"
+ },
+ {
+ "active" : false,
+ "scopes" : [
+ "read_user"
+ ],
+ "revoked" : true,
+ "token" : "ZcZRpLeEuQRprkRjYydY",
+ "name" : "mytoken2",
+ "created_at" : "2017-03-17T17:19:28.697Z",
+ "id" : 3,
+ "impersonation" : true,
+ "expires_at" : "2017-04-14"
+ }
]
```
-## Show a user's impersonation token
+## Get an impersonation token of a user
-It shows a user's impersonation token. Note that only administrators can do this.
+> Requires admin permissions.
+
+It shows a user's impersonation token.
```
GET /users/:user_id/impersonation_tokens/:impersonation_token_id
@@ -875,7 +900,31 @@ Parameters:
| `user_id` | integer | yes | The ID of the user |
| `impersonation_token_id` | integer | yes | The ID of the impersonation token |
-## Create a impersonation token
+```
+curl --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/impersonation_tokens/2
+```
+
+Example response:
+
+```json
+{
+ "active" : true,
+ "token" : "EsMo-vhKfXGwX9RKrwiy",
+ "scopes" : [
+ "api"
+ ],
+ "revoked" : false,
+ "name" : "mytoken",
+ "id" : 2,
+ "created_at" : "2017-03-17T17:18:09.283Z",
+ "impersonation" : true,
+ "expires_at" : "2017-04-04"
+}
+```
+
+## Create an impersonation token
+
+> Requires admin permissions.
It creates a new impersonation token. Note that only administrators can do this.
You are only able to create impersonation tokens to impersonate the user and perform
@@ -891,32 +940,46 @@ Parameters:
| Attribute | Type | Required | Description |
| --------- | ---- | -------- | ----------- |
| `user_id` | integer | yes | The ID of the user |
-| `name` | string | yes | The name of the impersonation token |
-| `expires_at` | date | no | The expiration date of the impersonation token |
-| `scopes` | array | no | The array of scopes of the impersonation token (api, read_user) |
+| `name` | string | yes | The name of the impersonation token |
+| `expires_at` | date | no | The expiration date of the impersonation token in ISO format (`YYYY-MM-DD`)|
+| `scopes` | array | yes | The array of scopes of the impersonation token (`api`, `read_user`) |
+
+```
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "name=mytoken" --data "expires_at=2017-04-04" --data "scopes[]=api" https://gitlab.example.com/api/v4/users/42/impersonation_tokens
+```
Example response:
+
```json
{
- "id": 1,
- "name": "mytoken",
- "revoked": false,
- "expires_at": "2017-01-04",
- "scopes": ['api'],
- "active": true,
- "impersonation": true,
- "token": "9koXpg98eAheJpvBs5tK"
+ "id" : 2,
+ "revoked" : false,
+ "scopes" : [
+ "api"
+ ],
+ "token" : "EsMo-vhKfXGwX9RKrwiy",
+ "active" : true,
+ "impersonation" : true,
+ "name" : "mytoken",
+ "created_at" : "2017-03-17T17:18:09.283Z",
+ "expires_at" : "2017-04-04"
}
```
## Revoke an impersonation token
-It revokes an impersonation token. Note that only administrators can revoke impersonation tokens.
+> Requires admin permissions.
+
+It revokes an impersonation token.
```
DELETE /users/:user_id/impersonation_tokens/:impersonation_token_id
```
+```
+curl --request DELETE --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/users/42/impersonation_tokens/1
+```
+
Parameters:
| Attribute | Type | Required | Description |
diff --git a/doc/api/v3_to_v4.md b/doc/api/v3_to_v4.md
index 7f4426ee85d..8e002fe0022 100644
--- a/doc/api/v3_to_v4.md
+++ b/doc/api/v3_to_v4.md
@@ -80,3 +80,4 @@ Below are the changes made between V3 and V4.
- `GET /projects/:id/repository/blobs/:sha` now returns JSON attributes for the blob identified by `:sha`, instead of finding the commit identified by `:sha` and returning the raw content of the blob in that commit identified by the required `?filepath=:filepath`
- Moved `GET /projects/:id/repository/commits/:sha/blob?file_path=:file_path` and `GET /projects/:id/repository/blobs/:sha?file_path=:file_path` to `GET /projects/:id/repository/files/:file_path/raw?ref=:sha`
- `GET /projects/:id/repository/tree` parameter `ref_name` has been renamed to `ref` for consistency
+- `confirm` parameter for `POST /users` has been deprecated in favor of `skip_confirmation` parameter
diff --git a/doc/development/README.md b/doc/development/README.md
index 265df98fb87..e27e7fc7d19 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -15,7 +15,7 @@
- [SQL Migration Style Guide](migration_style_guide.md) for creating safe SQL migrations
- [Testing standards and style guidelines](testing.md)
- [UX guide](ux_guide/index.md) for building GitLab with existing CSS styles and elements
-- [Frontend guidelines](frontend.md)
+- [Frontend guidelines](fe_guide/index.md)
- [SQL guidelines](sql.md) for working with SQL queries
- [Sidekiq guidelines](sidekiq_style_guide.md) for working with Sidekiq workers
- [`Gemfile` guidelines](gemfile.md)
diff --git a/doc/development/ci_setup.md b/doc/development/ci_setup.md
index b03216fec95..0810b32efd7 100644
--- a/doc/development/ci_setup.md
+++ b/doc/development/ci_setup.md
@@ -7,7 +7,7 @@ We currently use four CI services to test GitLab:
1. GitLab CI on [GitHost.io](https://gitlab-ce.githost.io/projects/4/) for the [GitLab.com repo](https://gitlab.com/gitlab-org/gitlab-ce)
2. GitLab CI at ci.gitlab.org to test the private GitLab B.V. repo at dev.gitlab.org
3. [Semephore](https://semaphoreapp.com/gitlabhq/gitlabhq/) for [GitHub.com repo](https://github.com/gitlabhq/gitlabhq)
-4. [Mock CI Service](user/project/integrations/mock_ci.md) for local development
+4. [Mock CI Service](../user/project/integrations/mock_ci.md) for local development
| Software @ configuration being tested | GitLab CI (ci.gitlab.org) | GitLab CI (GitHost.io) | Semaphore |
|---------------------------------------|---------------------------|---------------------------------------------------------------------------|-----------|
diff --git a/doc/development/fe_guide/accessibility.md b/doc/development/fe_guide/accessibility.md
new file mode 100644
index 00000000000..366b220cbb2
--- /dev/null
+++ b/doc/development/fe_guide/accessibility.md
@@ -0,0 +1,13 @@
+# Accessibility
+
+## Resources
+
+[Chrome Accessibility Developer Tools][chrome-accessibility-developer-tools]
+are useful for testing for potential accessibility problems in GitLab.
+
+Accessibility best-practices and more in-depth information is available on
+[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools.
+
+
+[chrome-accessibility-developer-tools]: https://github.com/GoogleChrome/accessibility-developer-tools
+[audit-rules]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules
diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md
new file mode 100644
index 00000000000..aebb22caa15
--- /dev/null
+++ b/doc/development/fe_guide/architecture.md
@@ -0,0 +1,22 @@
+# Architecture
+
+When you are developing a new feature that requires architectural design, or if
+you are changing the fundamental design of an existing feature, make sure it is
+discussed with one of the Frontend Architecture Experts.
+
+A Frontend Architect is an expert who makes high-level Frontend design decisions
+and decides on technical standards, including coding standards and frameworks.
+
+Architectural decisions should be accessible to everyone, so please document
+them in the relevant Merge Request discussion or by updating our documentation
+when appropriate.
+
+You can find the Frontend Architecture experts on the [team page][team-page].
+
+## Examples
+
+You can find documentation about the desired architecture for a new feature
+built with Vue.js [here][vue-section].
+
+[team-page]: https://about.gitlab.com/team
+[vue-section]: vue.md#frontend.html#how-to-build-a-new-feature-with-vue-js
diff --git a/doc/development/fe_guide/design_patterns.md b/doc/development/fe_guide/design_patterns.md
new file mode 100644
index 00000000000..e05887a19af
--- /dev/null
+++ b/doc/development/fe_guide/design_patterns.md
@@ -0,0 +1,78 @@
+# Design Patterns
+
+## Singletons
+
+When exactly one object is needed for a given task, prefer to define it as a
+`class` rather than as an object literal. Prefer also to explicitly restrict
+instantiation, unless flexibility is important (e.g. for testing).
+
+```javascript
+// bad
+
+const MyThing = {
+ prop1: 'hello',
+ method1: () => {}
+};
+
+export default MyThing;
+
+// good
+
+class MyThing {
+ constructor() {
+ this.prop1 = 'hello';
+ }
+ method1() {}
+}
+
+export default new MyThing();
+
+// best
+
+export default class MyThing {
+ constructor() {
+ if (!this.prototype.singleton) {
+ this.init();
+ this.prototype.singleton = this;
+ }
+ return this.prototype.singleton;
+ }
+
+ init() {
+ this.prop1 = 'hello';
+ }
+
+ method1() {}
+}
+
+```
+
+## Manipulating the DOM in a JS Class
+
+When writing a class that needs to manipulate the DOM guarantee a container option is provided.
+This is useful when we need that class to be instantiated more than once in the same page.
+
+Bad:
+```javascript
+class Foo {
+ constructor() {
+ document.querySelector('.bar');
+ }
+}
+new Foo();
+```
+
+Good:
+```javascript
+class Foo {
+ constructor(opts) {
+ document.querySelector(`${opts.container} .bar`);
+ }
+}
+
+new Foo({ container: '.my-element' });
+```
+You can find an example of the above in this [class][container-class-example];
+
+
+[container-class-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/mini_pipeline_graph_dropdown.js
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
new file mode 100644
index 00000000000..f963bffde37
--- /dev/null
+++ b/doc/development/fe_guide/index.md
@@ -0,0 +1,92 @@
+# Frontend Development Guidelines
+
+This document describes various guidelines to ensure consistency and quality
+across GitLab's frontend team.
+
+## Overview
+
+GitLab is built on top of [Ruby on Rails][rails] using [Haml][haml] with
+[Hamlit][hamlit]. Be wary of [the limitations that come with using
+Hamlit][hamlit-limits]. We also use [SCSS][scss] and plain JavaScript with
+modern ECMAScript standards supported through [Babel][babel] and ES module
+support through [webpack][webpack].
+
+We also utilize [webpack][webpack] to handle the bundling, minification, and
+compression of our assets.
+
+Working with our frontend assets requires Node (v4.3 or greater) and Yarn
+(v0.17 or greater). You can find information on how to install these on our
+[installation guide][install].
+
+[jQuery][jquery] is used throughout the application's JavaScript, with
+[Vue.js][vue] for particularly advanced, dynamic elements.
+
+### Browser Support
+
+For our currently-supported browsers, see our [requirements][requirements].
+
+---
+
+## [Architecture](architecture.md)
+How we go about making fundamental design decisions in GitLab's frontend team
+or make changes to our frontend development guidelines.
+
+---
+
+## [Testing](testing.md)
+How we write frontend tests, run the GitLab test suite, and debug test related
+issues.
+
+---
+
+## [Design Patterns](design_patterns.md)
+Common JavaScript design patterns in GitLab's codebase.
+
+---
+
+## [Vue.js Best Practices](vue.md)
+Vue specific design patterns and practices.
+
+---
+
+## Style Guides
+
+### [JavaScript Style Guide](style_guide_js.md)
+
+We use eslint to enforce our JavaScript style guides. Our guide is based on
+the excellent [Airbnb][airbnb-js-style-guide] style guide with a few small
+changes.
+
+### [SCSS Style Guide](style_guide_scss.md)
+
+Our SCSS conventions which are enforced through [scss-lint][scss-lint].
+
+---
+
+## [Performance](performance.md)
+Best practices for monitoring and maximizing frontend performance.
+
+---
+
+## [Security](security.md)
+Frontend security practices.
+
+---
+
+## [Accessibility](accessibility.md)
+Our accessibility standards and resources.
+
+
+[rails]: http://rubyonrails.org/
+[haml]: http://haml.info/
+[hamlit]: https://github.com/k0kubun/hamlit
+[hamlit-limits]: https://github.com/k0kubun/hamlit/blob/master/REFERENCE.md#limitations
+[scss]: http://sass-lang.com/
+[babel]: https://babeljs.io/
+[webpack]: https://webpack.js.org/
+[jquery]: https://jquery.com/
+[vue]: http://vuejs.org/
+[airbnb-js-style-guide]: https://github.com/airbnb/javascript
+[scss-lint]: https://github.com/brigade/scss-lint
+[install]: ../../install/installation.md#4-node
+[requirements]: ../../install/requirements.md#supported-web-browsers
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
new file mode 100644
index 00000000000..2d76bb18cff
--- /dev/null
+++ b/doc/development/fe_guide/performance.md
@@ -0,0 +1,95 @@
+# Performance
+
+## Best Practices
+
+### Realtime Components
+
+When writing code for realtime features we have to keep a couple of things in mind:
+1. Do not overload the server with requests.
+1. It should feel realtime.
+
+Thus, we must strike a balance between sending requests and the feeling of realtime.
+Use the following rules when creating realtime solutions.
+
+1. The server will tell you how much to poll by sending `Poll-Interval` in the header.
+Use that as your polling interval. This way it is easy for system administrators to change the
+polling rate.
+A `Poll-Interval: -1` means you should disable polling, and this must be implemented.
+1. A response with HTTP status `4XX` or `5XX` should disable polling as well.
+1. Use a common library for polling.
+1. Poll on active tabs only. Use a common library to find out which tab currently has eyes on it.
+Please use [Focus](https://gitlab.com/andrewn/focus). Specifically [Eyeballs Detector](https://gitlab.com/andrewn/focus/blob/master/lib/eyeballs-detector.js).
+1. Use regular polling intervals, do not use backoff polling, or jitter, as the interval will be
+controlled by the server.
+1. The backend code will most likely be using etags. You do not and should not check for status
+`304 Not Modified`. The browser will transform it for you.
+
+## Reducing Asset Footprint
+
+### Page-specific JavaScript
+
+Certain pages may require the use of a third party library, such as [d3][d3] for
+the User Activity Calendar and [Chart.js][chartjs] for the Graphs pages. These
+libraries increase the page size significantly, and impact load times due to
+bandwidth bottlenecks and the browser needing to parse more JavaScript.
+
+In cases where libraries are only used on a few specific pages, we use
+"page-specific JavaScript" to prevent the main `main.js` file from
+becoming unnecessarily large.
+
+Steps to split page-specific JavaScript from the main `main.js`:
+
+1. Create a directory for the specific page(s), e.g. `graphs/`.
+1. In that directory, create a `namespace_bundle.js` file, e.g. `graphs_bundle.js`.
+1. Add the new "bundle" file to the list of entry files in `config/webpack.config.js`.
+ - For example: `graphs: './graphs/graphs_bundle.js',`.
+1. Move code reliant on these libraries into the `graphs` directory.
+1. In `graphs_bundle.js` add CommonJS `require('./path_to_some_component.js');` statements to load any other files in this directory. Make sure to use relative urls.
+1. In the relevant views, add the scripts to the page with the following:
+
+```haml
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_bundle_tag('lib_chart')
+ = page_specific_javascript_bundle_tag('graphs')
+```
+
+The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js`
+is separated from the bundle file so it can be cached separately from the bundle
+and reused for other pages that also rely on the library. For an example, see
+[this Haml file][page-specific-js-example].
+
+### Code Splitting
+
+> *TODO* flesh out this section once webpack is ready for code-splitting
+
+### Minimizing page size
+
+A smaller page size means the page loads faster (especially important on mobile
+and poor connections), the page is parsed more quickly by the browser, and less
+data is used for users with capped data plans.
+
+General tips:
+
+- Don't add new fonts.
+- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF.
+- Compress and minify assets wherever possible (For CSS/JS, Sprockets and webpack do this for us).
+- If some functionality can reasonably be achieved without adding extra libraries, avoid them.
+- Use page-specific JavaScript as described above to dynamically load libraries that are only needed on certain pages.
+
+-------
+
+## Additional Resources
+
+- [WebPage Test][web-page-test] for testing site loading time and size.
+- [Google PageSpeed Insights][pagespeed-insights] grades web pages and provides feedback to improve the page.
+- [Profiling with Chrome DevTools][google-devtools-profiling]
+- [Browser Diet][browser-diet] is a community-built guide that catalogues practical tips for improving web page performance.
+
+
+[web-page-test]: http://www.webpagetest.org/
+[pagespeed-insights]: https://developers.google.com/speed/pagespeed/insights/
+[google-devtools-profiling]: https://developers.google.com/web/tools/chrome-devtools/profile/?hl=en
+[browser-diet]: https://browserdiet.com/
+[d3]: https://d3js.org/
+[chartjs]: http://www.chartjs.org/
+[page-specific-js-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/13bb9ed77f405c5f6ee4fdbc964ecf635c9a223f/app/views/projects/graphs/_head.html.haml#L6-8
diff --git a/doc/development/fe_guide/security.md b/doc/development/fe_guide/security.md
new file mode 100644
index 00000000000..19e72c1d368
--- /dev/null
+++ b/doc/development/fe_guide/security.md
@@ -0,0 +1,92 @@
+# Security
+### Resources
+
+[Mozilla’s HTTP Observatory CLI][observatory-cli] and the
+[Qualys SSL Labs Server Test][qualys-ssl] are good resources for finding
+potential problems and ensuring compliance with security best practices.
+
+<!-- Uncomment these sections when CSP/SRI are implemented.
+### Content Security Policy (CSP)
+
+Content Security Policy is a web standard that intends to mitigate certain
+forms of Cross-Site Scripting (XSS) as well as data injection.
+
+Content Security Policy rules should be taken into consideration when
+implementing new features, especially those that may rely on connection with
+external services.
+
+GitLab's CSP is used for the following:
+
+- Blocking plugins like Flash and Silverlight from running at all on our pages.
+- Blocking the use of scripts and stylesheets downloaded from external sources.
+- Upgrading `http` requests to `https` when possible.
+- Preventing `iframe` elements from loading in most contexts.
+
+Some exceptions include:
+
+- Scripts from Google Analytics and Piwik if either is enabled.
+- Connecting with GitHub, Bitbucket, GitLab.com, etc. to allow project importing.
+- Connecting with Google, Twitter, GitHub, etc. to allow OAuth authentication.
+
+We use [the Secure Headers gem][secure_headers] to enable Content
+Security Policy headers in the GitLab Rails app.
+
+Some resources on implementing Content Security Policy:
+
+- [MDN Article on CSP][mdn-csp]
+- [GitHub’s CSP Journey on the GitHub Engineering Blog][github-eng-csp]
+- The Dropbox Engineering Blog's series on CSP: [1][dropbox-csp-1], [2][dropbox-csp-2], [3][dropbox-csp-3], [4][dropbox-csp-4]
+
+### Subresource Integrity (SRI)
+
+Subresource Integrity prevents malicious assets from being provided by a CDN by
+guaranteeing that the asset downloaded is identical to the asset the server
+is expecting.
+
+The Rails app generates a unique hash of the asset, which is used as the
+asset's `integrity` attribute. The browser generates the hash of the asset
+on-load and will reject the asset if the hashes do not match.
+
+All CSS and JavaScript assets should use Subresource Integrity.
+
+Some resources on implementing Subresource Integrity:
+
+- [MDN Article on SRI][mdn-sri]
+- [Subresource Integrity on the GitHub Engineering Blog][github-eng-sri]
+
+-->
+
+### Including external resources
+
+External fonts, CSS, and JavaScript should never be used with the exception of
+Google Analytics and Piwik - and only when the instance has enabled it. Assets
+should always be hosted and served locally from the GitLab instance. Embedded
+resources via `iframes` should never be used except in certain circumstances
+such as with ReCaptcha, which cannot be used without an `iframe`.
+
+### Avoiding inline scripts and styles
+
+In order to protect users from [XSS vulnerabilities][xss], we will disable
+inline scripts in the future using Content Security Policy.
+
+While inline scripts can be useful, they're also a security concern. If
+user-supplied content is unintentionally left un-sanitized, malicious users can
+inject scripts into the web app.
+
+Inline styles should be avoided in almost all cases, they should only be used
+when no alternatives can be found. This allows reusability of styles as well as
+readability.
+
+
+[observatory-cli]: https://github.com/mozilla/http-observatory-cli
+[qualys-ssl]: https://www.ssllabs.com/ssltest/analyze.html
+[secure_headers]: https://github.com/twitter/secureheaders
+[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/Security/CSP
+[github-eng-csp]: http://githubengineering.com/githubs-csp-journey/
+[dropbox-csp-1]: https://blogs.dropbox.com/tech/2015/09/on-csp-reporting-and-filtering/
+[dropbox-csp-2]: https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/
+[dropbox-csp-3]: https://blogs.dropbox.com/tech/2015/09/csp-the-unexpected-eval/
+[dropbox-csp-4]: https://blogs.dropbox.com/tech/2015/09/csp-third-party-integrations-and-privilege-separation/
+[mdn-sri]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
+[github-eng-sri]: http://githubengineering.com/subresource-integrity/
+[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
new file mode 100644
index 00000000000..034cfe73d33
--- /dev/null
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -0,0 +1,408 @@
+# Style guides and linting
+See the relevant style guides for our guidelines and for information on linting:
+
+## JavaScript
+We defer to [Airbnb][airbnb-js-style-guide] on most style-related
+conventions and enforce them with eslint.
+
+See [our current .eslintrc][eslintrc] for specific rules and patterns.
+
+### Common
+
+#### ESlint
+
+- **Never** disable eslint rules unless you have a good reason. You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */` at the top, but legacy files are a special case. Any time you develop a new feature or refactor an existing one, you should abide by the eslint rules.
+
+- **Never Ever EVER** disable eslint globally for a file
+
+ ```javascript
+ // bad
+ /* eslint-disable */
+
+ // better
+ /* eslint-disable some-rule, some-other-rule */
+
+ // best
+ // nothing :)
+ ```
+
+- If you do need to disable a rule for a single violation, try to do it as locally as possible
+
+ ```javascript
+ // bad
+ /* eslint-disable no-new */
+
+ import Foo from 'foo';
+
+ new Foo();
+
+ // better
+ import Foo from 'foo';
+
+ // eslint-disable-next-line no-new
+ new Foo();
+ ```
+
+- When they are needed _always_ place ESlint directive comment blocks on the first line of a script, followed by any global declarations, then a blank newline prior to any imports or code.
+
+ ```javascript
+ // bad
+ /* global Foo */
+ /* eslint-disable no-new */
+ import Bar from './bar';
+
+ // good
+ /* eslint-disable no-new */
+ /* global Foo */
+
+ import Bar from './bar';
+ ```
+
+- **Never** disable the `no-undef` rule. Declare globals with `/* global Foo */` instead.
+
+- When declaring multiple globals, always use one `/* global [name] */` line per variable.
+
+ ```javascript
+ // bad
+ /* globals Flash, Cookies, jQuery */
+
+ // good
+ /* global Flash */
+ /* global Cookies */
+ /* global jQuery */
+ ```
+
+#### Modules, Imports, and Exports
+- Use ES module syntax to import modules
+
+ ```javascript
+ // bad
+ require('foo');
+
+ // good
+ import Foo from 'foo';
+
+ // bad
+ module.exports = Foo;
+
+ // good
+ export default Foo;
+ ```
+
+- Relative paths
+
+ Unless you are writing a test, always reference other scripts using relative paths instead of `~`
+
+ In **app/assets/javascripts**:
+ ```javascript
+ // bad
+ import Foo from '~/foo'
+
+ // good
+ import Foo from '../foo';
+ ```
+
+ In **spec/javascripts**:
+ ```javascript
+ // bad
+ import Foo from '../../app/assets/javascripts/foo'
+
+ // good
+ import Foo from '~/foo';
+ ```
+
+- Avoid using IIFE. Although we have a lot of examples of files which wrap their contents in IIFEs (immediately-invoked function expressions), this is no longer necessary after the transition from Sprockets to webpack. Do not use them anymore and feel free to remove them when refactoring legacy code.
+
+- Avoid adding to the global namespace.
+
+ ```javascript
+ // bad
+ window.MyClass = class { /* ... */ };
+
+ // good
+ export default class MyClass { /* ... */ }
+ ```
+
+- Side effects are forbidden in any script which contains exports
+
+ ```javascript
+ // bad
+ export default class MyClass { /* ... */ }
+
+ document.addEventListener("DOMContentLoaded", function(event) {
+ new MyClass();
+ }
+ ```
+
+
+#### Data Mutation and Pure functions
+- Strive to write many small pure functions, and minimize where mutations occur.
+
+ ```javascript
+ // bad
+ const values = {foo: 1};
+
+ function impureFunction(items) {
+ const bar = 1;
+
+ items.foo = items.a * bar + 2;
+
+ return items.a;
+ }
+
+ const c = impureFunction(values);
+
+ // good
+ var values = {foo: 1};
+
+ function pureFunction (foo) {
+ var bar = 1;
+
+ foo = foo * bar + 2;
+
+ return foo;
+ }
+
+ var c = pureFunction(values.foo);
+ ```
+
+- Avoid constructors with side-effects
+
+
+#### Parse Strings into Numbers
+- `parseInt()` is preferable over `Number()` or `+`
+
+ ```javascript
+ // bad
+ +'10' // 10
+
+ // good
+ Number('10') // 10
+
+ // better
+ parseInt('10', 10);
+ ```
+
+
+### Vue.js
+
+
+#### Basic Rules
+- Only include one Vue.js component per file.
+- Export components as plain objects:
+
+ ```javascript
+ export default {
+ template: `<h1>I'm a component</h1>
+ }
+ ```
+
+#### Naming
+- **Extensions**: Use `.vue` extension for Vue components.
+- **Reference Naming**: Use PascalCase for Vue components and camelCase for their instances:
+
+ ```javascript
+ // bad
+ import cardBoard from 'cardBoard';
+
+ // good
+ import CardBoard from 'cardBoard'
+
+ // bad
+ components: {
+ CardBoard: CardBoard
+ };
+
+ // good
+ components: {
+ cardBoard: CardBoard
+ };
+ ```
+- **Props Naming**: Avoid using DOM component prop names.
+
+ ```javascript
+ // bad
+ <component class="btn">
+
+ // good
+ <component cssClass="btn">
+ ```
+
+#### Alignment
+- Follow these alignment styles for the template method:
+
+ ```javascript
+ // bad
+ <component v-if="bar"
+ param="baz" />
+
+ // good
+ <component
+ v-if="bar"
+ param="baz"
+ />
+
+ // if props fit in one line then keep it on the same line
+ <component bar="bar" />
+ ```
+
+#### Quotes
+- Always use double quotes `"` inside templates and single quotes `'` for all other JS.
+
+ ```javascript
+ // bad
+ template: `
+ <button :class='style'>Button</button>
+ `
+
+ // good
+ template: `
+ <button :class="style">Button</button>
+ `
+ ```
+
+#### Props
+- Props should be declared as an object
+
+ ```javascript
+ // bad
+ props: ['foo']
+
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
+ }
+ }
+ ```
+
+- Required key should always be provided when declaring a prop
+
+ ```javascript
+ // bad
+ props: {
+ foo: {
+ type: String,
+ }
+ }
+
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
+ }
+ }
+ ```
+
+- Default key should always be provided if the prop is not required:
+
+ ```javascript
+ // bad
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ }
+ }
+
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: false,
+ default: 'bar'
+ }
+ }
+
+ // good
+ props: {
+ foo: {
+ type: String,
+ required: true
+ }
+ }
+ ```
+
+#### Data
+- `data` method should always be a function
+
+ ```javascript
+ // bad
+ data: {
+ foo: 'foo'
+ }
+
+ // good
+ data() {
+ return {
+ foo: 'foo'
+ };
+ }
+ ```
+
+#### Directives
+
+- Shorthand `@` is preferable over `v-on`
+
+ ```javascript
+ // bad
+ <component v-on:click="eventHandler"/>
+
+
+ // good
+ <component @click="eventHandler"/>
+ ```
+
+- Shorthand `:` is preferable over `v-bind`
+
+ ```javascript
+ // bad
+ <component v-bind:class="btn"/>
+
+
+ // good
+ <component :class="btn"/>
+ ```
+
+#### Closing tags
+- Prefer self closing component tags
+
+ ```javascript
+ // bad
+ <component></component>
+
+ // good
+ <component />
+ ```
+
+#### Ordering
+- Order for a Vue Component:
+ 1. `name`
+ 2. `props`
+ 3. `data`
+ 4. `components`
+ 5. `computedProps`
+ 6. `methods`
+ 7. lifecycle methods
+ 1. `beforeCreate`
+ 2. `created`
+ 3. `beforeMount`
+ 4. `mounted`
+ 5. `beforeUpdate`
+ 6. `updated`
+ 7. `activated`
+ 8. `deactivated`
+ 9. `beforeDestroy`
+ 10. `destroyed`
+ 8. `template`
+
+
+## SCSS
+- [SCSS](style_guide_scss.md)
+
+[airbnb-js-style-guide]: https://github.com/airbnb/javascript
+[eslintrc]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc
diff --git a/doc/development/scss_styleguide.md b/doc/development/fe_guide/style_guide_scss.md
index a79f4073cde..77b308c4a43 100644
--- a/doc/development/scss_styleguide.md
+++ b/doc/development/fe_guide/style_guide_scss.md
@@ -35,7 +35,7 @@ between the property and its value.
```scss
// Bad
-.container-item {
+.container-item {
width: 100px; height: 100px;
margin-top: 0;
}
@@ -63,7 +63,7 @@ between the property and its value.
}
```
-Note that there is an exception for single-line rulesets, although these are
+Note that there is an exception for single-line rulesets, although these are
not typically recommended.
```scss
@@ -72,7 +72,7 @@ p { margin: 0; padding: 0; }
### Colors
-HEX (hexadecimal) colors should use shorthand where possible, and should use
+HEX (hexadecimal) colors should use shorthand where possible, and should use
lower case letters to differentiate between letters and numbers, e.g. `#E3E3E3`
vs. `#e3e3e3`.
@@ -111,7 +111,7 @@ p {
### Semicolons
-Always include semicolons after every property. When the stylesheets are
+Always include semicolons after every property. When the stylesheets are
minified, the semicolons will be removed automatically.
```scss
@@ -144,7 +144,7 @@ padding: 10px;
### Zero Units
-Omit length units on zero values, they're unnecessary and not including them
+Omit length units on zero values, they're unnecessary and not including them
is slightly more performant.
```scss
@@ -161,36 +161,56 @@ is slightly more performant.
### Selectors with a `js-` Prefix
-Do not use any selector prefixed with `js-` for styling purposes. These
-selectors are intended for use only with JavaScript to allow for removal or
+Do not use any selector prefixed with `js-` for styling purposes. These
+selectors are intended for use only with JavaScript to allow for removal or
renaming without breaking styling.
+### IDs
+Don't use ID selectors in CSS.
+
+```scss
+// Bad
+#my-element {
+ padding: 0;
+}
+
+// Good
+.my-element {
+ padding: 0;
+}
+```
+
+### Variables
+Before adding a new variable for a color or a size, guarantee:
+1. There isn't already one
+2. There isn't a similar one we can use instead.
+
## Linting
-We use [SCSS Lint][scss-lint] to check for style guide conformity. It uses the
-ruleset in `.scss-lint.yml`, which is located in the home directory of the
+We use [SCSS Lint][scss-lint] to check for style guide conformity. It uses the
+ruleset in `.scss-lint.yml`, which is located in the home directory of the
project.
-To check if any warnings will be produced by your changes, you can run `rake
-scss_lint` in the GitLab directory. SCSS Lint will also run in GitLab CI to
+To check if any warnings will be produced by your changes, you can run `rake
+scss_lint` in the GitLab directory. SCSS Lint will also run in GitLab CI to
catch any warnings.
-If the Rake task is throwing warnings you don't understand, SCSS Lint's
+If the Rake task is throwing warnings you don't understand, SCSS Lint's
documentation includes [a full list of their linters][scss-lint-documentation].
### Fixing issues
-If you want to automate changing a large portion of the codebase to conform to
+If you want to automate changing a large portion of the codebase to conform to
the SCSS style guide, you can use [CSSComb][csscomb]. First install
-[Node][node] and [NPM][npm], then run `npm install csscomb -g` to install
-CSSComb globally (system-wide). Run it in the GitLab directory with
+[Node][node] and [NPM][npm], then run `npm install csscomb -g` to install
+CSSComb globally (system-wide). Run it in the GitLab directory with
`csscomb app/assets/stylesheets` to automatically fix issues with CSS/SCSS.
Note that this won't fix every problem, but it should fix a majority.
### Ignoring issues
-If you want a line or set of lines to be ignored by the linter, you can use
+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
@@ -203,8 +223,8 @@ If you want a line or set of lines to be ignored by the linter, you can use
```
Make sure a comment is added on the line above the `disable` rule, otherwise the
-linter will throw a warning. `DisableLinterReason` is enabled to make sure the
-style guide isn't being ignored, and to communicate to others why the style
+linter will throw a warning. `DisableLinterReason` is enabled to make sure the
+style guide isn't being ignored, and to communicate to others why the style
guide is ignored in this instance.
[csscomb]: https://github.com/csscomb/csscomb.js
diff --git a/doc/development/fe_guide/testing.md b/doc/development/fe_guide/testing.md
new file mode 100644
index 00000000000..bb6adeacc4c
--- /dev/null
+++ b/doc/development/fe_guide/testing.md
@@ -0,0 +1,129 @@
+# Frontend Testing
+
+There are two types of tests you'll encounter while developing frontend code
+at GitLab. We use Karma and Jasmine for JavaScript unit testing, and RSpec
+feature tests with Capybara for integration testing.
+
+Feature tests need to be written for all new features. Regression tests ought
+to be written for all bug fixes to prevent them from recurring in the future.
+
+See [the Testing Standards and Style Guidelines](/doc/development/testing.md)
+for more information on general testing practices at GitLab.
+
+## Karma test suite
+
+GitLab uses the [Karma][karma] test runner with [Jasmine][jasmine] as its test
+framework for our JavaScript unit tests. For tests that rely on DOM
+manipulation we use fixtures which are pre-compiled from HAML source files and
+served during testing by the [jasmine-jquery][jasmine-jquery] plugin.
+
+### Running frontend tests
+
+`rake karma` runs the frontend-only (JavaScript) tests.
+It consists of two subtasks:
+
+- `rake karma:fixtures` (re-)generates fixtures
+- `rake karma:tests` actually executes the tests
+
+As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
+is sufficient (and saves you some time).
+
+### Live testing and focused testing
+
+While developing locally, it may be helpful to keep karma running so that you
+can get instant feedback on as you write tests and modify code. To do this
+you can start karma with `npm run karma-start`. It will compile the javascript
+assets and run a server at `http://localhost:9876/` where it will automatically
+run the tests on any browser which connects to it. You can enter that url on
+multiple browsers at once to have it run the tests on each in parallel.
+
+While karma is running, any changes you make will instantly trigger a recompile
+and retest of the entire test suite, so you can see instantly if you've broken
+a test with your changes. You can use [jasmine focused][jasmine-focus] or
+excluded tests (with `fdescribe` or `xdescribe`) to get karma to run only the
+tests you want while you're working on a specific feature, but make sure to
+remove these directives when you commit your code.
+
+## RSpec Feature Integration Tests
+
+Information on setting up and running RSpec integration tests with
+[Capybara][capybara] can be found in the
+[general testing guide](/doc/development/testing.md).
+
+## Gotchas
+
+### Errors due to use of unsupported JavaScript features
+
+Similar errors will be thrown if you're using JavaScript features not yet
+supported by the PhantomJS test runner which is used for both Karma and RSpec
+tests. We polyfill some JavaScript objects for older browsers, but some
+features are still unavailable:
+
+- Array.from
+- Array.first
+- Async functions
+- Generators
+- Array destructuring
+- For..Of
+- Symbol/Symbol.iterator
+- Spread
+
+Until these are polyfilled appropriately, they should not be used. Please
+update this list with additional unsupported features.
+
+### RSpec errors due to JavaScript
+
+By default RSpec unit tests will not run JavaScript in the headless browser
+and will simply rely on inspecting the HTML generated by rails.
+
+If an integration test depends on JavaScript to run correctly, you need to make
+sure the spec is configured to enable JavaScript when the tests are run. If you
+don't do this you'll see vague error messages from the spec runner.
+
+To enable a JavaScript driver in an `rspec` test, add `js: true` to the
+individual spec or the context block containing multiple specs that need
+JavaScript enabled:
+
+```ruby
+
+# For one spec
+it 'presents information about abuse report', js: true do
+ # assertions...
+end
+
+describe "Admin::AbuseReports", js: true do
+ it 'presents information about abuse report' do
+ # assertions...
+ end
+ it 'shows buttons for adding to abuse report' do
+ # assertions...
+ end
+end
+```
+
+### Spinach errors due to missing JavaScript
+
+> **Note:** Since we are discouraging the use of Spinach when writing new
+> feature tests, you shouldn't ever need to use this. This information is kept
+> available for legacy purposes only.
+
+In Spinach, the JavaScript driver is enabled differently. In the `*.feature`
+file for the failing spec, add the `@javascript` flag above the Scenario:
+
+```
+@javascript
+Scenario: Developer can approve merge request
+ Given I am a "Shop" developer
+ And I visit project "Shop" merge requests page
+ And merge request 'Bug NS-04' must be approved
+ And I click link "Bug NS-04"
+ When I click link "Approve"
+ Then I should see approved merge request "Bug NS-04"
+
+```
+
+[capybara]: http://teamcapybara.github.io/capybara/
+[jasmine]: https://jasmine.github.io/
+[jasmine-focus]: https://jasmine.github.io/2.5/focused_specs.html
+[jasmine-jquery]: https://github.com/velesin/jasmine-jquery
+[karma]: http://karma-runner.github.io/
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
new file mode 100644
index 00000000000..3e3406e7d6a
--- /dev/null
+++ b/doc/development/fe_guide/vue.md
@@ -0,0 +1,104 @@
+# Vue
+
+For more complex frontend features, we recommend using Vue.js. It shares
+some ideas with React.js as well as Angular.
+
+To get started with Vue, read through [their documentation][vue-docs].
+
+## When to use Vue.js
+
+We recommend using Vue for more complex features. Here are some guidelines for when to use Vue.js:
+
+- If you are starting a new feature or refactoring an old one that highly interacts with the DOM;
+- For real time data updates;
+- If you are creating a component that will be reused elsewhere;
+
+## When not to use Vue.js
+
+We don't want to refactor all GitLab frontend code into Vue.js, here are some guidelines for
+when not to use Vue.js:
+
+- Adding or changing static information;
+- Features that highly depend on jQuery will be hard to work with Vue.js
+
+As always, the Frontend Architectural Experts are available to help with any Vue or JavaScript questions.
+
+## How to build a new feature with Vue.js
+
+**Components, Stores and Services**
+
+In some features implemented with Vue.js, like the [issue board][issue-boards]
+or [environments table][environments-table]
+you can find a clear separation of concerns:
+
+```
+new_feature
+├── components
+│ └── component.js.es6
+│ └── ...
+├── store
+│ └── new_feature_store.js.es6
+├── service
+│ └── new_feature_service.js.es6
+├── new_feature_bundle.js.es6
+```
+_For consistency purposes, we recommend you to follow the same structure._
+
+Let's look into each of them:
+
+**A `*_bundle.js` file**
+
+This is the index file of your new feature. This is where the root Vue instance
+of the new feature should be.
+
+The Store and the Service should be imported and initialized in this file and provided as a prop to the main component.
+
+Don't forget to follow [these steps.][page_specific_javascript]
+
+**A folder for Components**
+
+This folder holds all components that are specific of this new feature.
+If you need to use or create a component that will probably be used somewhere
+else, please refer to `vue_shared/components`.
+
+A good thumb rule to know when you should create a component is to think if
+it will be reusable elsewhere.
+
+For example, tables are used in a quite amount of places across GitLab, a table
+would be a good fit for a component. On the other hand, a table cell used only
+in one table would not be a good use of this pattern.
+
+You can read more about components in Vue.js site, [Component System][component-system]
+
+**A folder for the Store**
+
+The Store is a class that allows us to manage the state in a single
+source of truth.
+
+The concept we are trying to follow is better explained by Vue documentation
+itself, please read this guide: [State Management][state-management]
+
+**A folder for the Service**
+
+The Service is used only to communicate with the server.
+It does not store or manipulate any data.
+We use [vue-resource][vue-resource-repo] to
+communicate with the server.
+
+The [issue boards service][issue-boards-service]
+is a good example of this pattern.
+
+## Style guide
+
+Please refer to the Vue section of our [style guide](style_guide_js.md#vuejs)
+for best practices while writing your Vue components and templates.
+
+
+[vue-docs]: http://vuejs.org/guide/index.html
+[issue-boards]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/boards
+[environments-table]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/environments
+[page_specific_javascript]: https://docs.gitlab.com/ce/development/frontend.html#page-specific-javascript
+[component-system]: https://vuejs.org/v2/guide/#Composing-with-Components
+[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
+[vue-resource-repo]: https://github.com/pagekit/vue-resource
+[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
diff --git a/doc/development/frontend.md b/doc/development/frontend.md
index 50105a486d0..f46cc377f95 100644
--- a/doc/development/frontend.md
+++ b/doc/development/frontend.md
@@ -1,511 +1,4 @@
-# Frontend Development Guidelines
-
-This document describes various guidelines to ensure consistency and quality
-across GitLab's frontend team.
-
-## Overview
-
-GitLab is built on top of [Ruby on Rails][rails] using [Haml][haml] with
-[Hamlit][hamlit]. Be wary of [the limitations that come with using
-Hamlit][hamlit-limits]. We also use [SCSS][scss] and plain JavaScript with
-[ES6 by way of Babel][es6].
-
-The asset pipeline is [Sprockets][sprockets], which handles the concatenation,
-minification, and compression of our assets.
-
-[jQuery][jquery] is used throughout the application's JavaScript, with
-[Vue.js][vue] for particularly advanced, dynamic elements.
-
-### Architecture
-
-The Frontend Architect is an expert who makes high-level frontend design choices
-and decides on technical standards, including coding standards, and frameworks.
-
-When you are assigned a new feature that requires architectural design,
-make sure it is discussed with one of the Frontend Architecture Experts.
-
-This rule also applies if you plan to change the architecture of an existing feature.
-
-These decisions should be accessible to everyone, so please document it on the Merge Request.
-
-You can find the Frontend Architecture experts on the [team page][team-page].
-
-You can find documentation about the desired architecture for a new feature built with Vue.js in [here][vue-section].
-
-### Realtime
-
-When writing code for realtime features we have to keep a couple of things in mind:
-1. Do not overload the server with requests.
-1. It should feel realtime.
-
-Thus, we must strike a balance between sending requests and the feeling of realtime.
-Use the following rules when creating realtime solutions.
-
-1. The server will tell you how much to poll by sending `Poll-Interval` in the header.
-Use that as your polling interval. This way it is easy for system administrators to change the
-polling rate.
-A `Poll-Interval: -1` means you should disable polling, and this must be implemented.
-1. A response with HTTP status `4XX` or `5XX` should disable polling as well.
-1. Use a common library for polling.
-1. Poll on active tabs only. Use a common library to find out which tab currently has eyes on it.
-Please use [Focus](https://gitlab.com/andrewn/focus). Specifically [Eyeballs Detector](https://gitlab.com/andrewn/focus/blob/master/lib/eyeballs-detector.js).
-1. Use regular polling intervals, do not use backoff polling, or jitter, as the interval will be
-controlled by the server.
-1. The backend code will most likely be using etags. You do not and should not check for status
-`304 Not Modified`. The browser will transform it for you.
-
-### Vue
-
-For more complex frontend features, we recommend using Vue.js. It shares
-some ideas with React.js as well as Angular.
-
-To get started with Vue, read through [their documentation][vue-docs].
-
-#### How to build a new feature with Vue.js
-**Components, Stores and Services**
-
-In some features implemented with Vue.js, like the [issue board][issue-boards]
-or [environments table][environments-table]
-you can find a clear separation of concerns:
-
-```
-new_feature
-├── components
-│ └── component.js.es6
-│ └── ...
-├── store
-│ └── new_feature_store.js.es6
-├── service
-│ └── new_feature_service.js.es6
-├── new_feature_bundle.js.es6
-```
-_For consistency purposes, we recommend you to follow the same structure._
-
-Let's look into each of them:
-
-**A `*_bundle.js` file**
-
-This is the index file of your new feature. This is where the root Vue instance
-of the new feature should be.
-
-The Store and the Service should be imported and initialized in this file and provided as a prop to the main component.
-
-Don't forget to follow [these steps.][page_specific_javascript]
-
-**A folder for Components**
-
-This folder holds all components that are specific of this new feature.
-If you need to use or create a component that will probably be used somewhere
-else, please refer to `vue_shared/components`.
-
-A good thumb rule to know when you should create a component is to think if
-it will be reusable elsewhere.
-
-For example, tables are used in a quite amount of places across GitLab, a table
-would be a good fit for a component.
-On the other hand, a table cell used only in on table, would not be a good use
-of this pattern.
-
-You can read more about components in Vue.js site, [Component System][component-system]
-
-**A folder for the Store**
-
-The Store is a class that allows us to manage the state in a single
-source of truth.
-
-The concept we are trying to follow is better explained by Vue documentation
-itself, please read this guide: [State Management][state-management]
-
-**A folder for the Service**
-
-The Service is used only to communicate with the server.
-It does not store or manipulate any data.
-We use [vue-resource][vue-resource-repo] to
-communicate with the server.
-
-The [issue boards service][issue-boards-service]
-is a good example of this pattern.
-
-## Performance
-
-### Resources
-
-- [WebPage Test][web-page-test] for testing site loading time and size.
-- [Google PageSpeed Insights][pagespeed-insights] grades web pages and provides feedback to improve the page.
-- [Profiling with Chrome DevTools][google-devtools-profiling]
-- [Browser Diet][browser-diet] is a community-built guide that catalogues practical tips for improving web page performance.
-
-### Page-specific JavaScript
-
-Certain pages may require the use of a third party library, such as [d3][d3] for
-the User Activity Calendar and [Chart.js][chartjs] for the Graphs pages. These
-libraries increase the page size significantly, and impact load times due to
-bandwidth bottlenecks and the browser needing to parse more JavaScript.
-
-In cases where libraries are only used on a few specific pages, we use
-"page-specific JavaScript" to prevent the main `application.js` file from
-becoming unnecessarily large.
-
-Steps to split page-specific JavaScript from the main `application.js`:
-
-1. Create a directory for the specific page(s), e.g. `graphs/`.
-1. In that directory, create a `namespace_bundle.js` file, e.g. `graphs_bundle.js`.
-1. In `graphs_bundle.js` add the line `//= require_tree .`, this adds all other files in the directory to the bundle.
-1. Add any necessary libraries to `app/assets/javascripts/lib/`, all files directly descendant from this directory will be precompiled as separate assets, in this case `chart.js` would be added.
-1. Add the new "bundle" file to the list of precompiled assets in
-`config/application.rb`.
- - For example: `config.assets.precompile << "graphs/graphs_bundle.js"`.
-1. Move code reliant on these libraries into the `graphs` directory.
-1. In the relevant views, add the scripts to the page with the following:
-
-```haml
-- content_for :page_specific_javascripts do
- = page_specific_javascript_tag('lib/chart.js')
- = page_specific_javascript_tag('graphs/graphs_bundle.js')
-```
-
-The above loads `chart.js` and `graphs_bundle.js` for this page only. `chart.js`
-is separated from the bundle file so it can be cached separately from the bundle
-and reused for other pages that also rely on the library. For an example, see
-[this Haml file][page-specific-js-example].
-
-### Minimizing page size
-
-A smaller page size means the page loads faster (especially important on mobile
-and poor connections), the page is parsed more quickly by the browser, and less
-data is used for users with capped data plans.
-
-General tips:
-
-- Don't add new fonts.
-- Prefer font formats with better compression, e.g. WOFF2 is better than WOFF, which is better than TTF.
-- Compress and minify assets wherever possible (For CSS/JS, Sprockets does this for us).
-- If some functionality can reasonably be achieved without adding extra libraries, avoid them.
-- Use page-specific JavaScript as described above to dynamically load libraries that are only needed on certain pages.
-
-## Accessibility
-
-### Resources
-
-[Chrome Accessibility Developer Tools][chrome-accessibility-developer-tools]
-are useful for testing for potential accessibility problems in GitLab.
-
-Accessibility best-practices and more in-depth information is available on
-[the Audit Rules page][audit-rules] for the Chrome Accessibility Developer Tools.
-
-## Security
-
-### Resources
-
-[Mozilla’s HTTP Observatory CLI][observatory-cli] and the
-[Qualys SSL Labs Server Test][qualys-ssl] are good resources for finding
-potential problems and ensuring compliance with security best practices.
-
-<!-- Uncomment these sections when CSP/SRI are implemented.
-### Content Security Policy (CSP)
-
-Content Security Policy is a web standard that intends to mitigate certain
-forms of Cross-Site Scripting (XSS) as well as data injection.
-
-Content Security Policy rules should be taken into consideration when
-implementing new features, especially those that may rely on connection with
-external services.
-
-GitLab's CSP is used for the following:
-
-- Blocking plugins like Flash and Silverlight from running at all on our pages.
-- Blocking the use of scripts and stylesheets downloaded from external sources.
-- Upgrading `http` requests to `https` when possible.
-- Preventing `iframe` elements from loading in most contexts.
-
-Some exceptions include:
-
-- Scripts from Google Analytics and Piwik if either is enabled.
-- Connecting with GitHub, Bitbucket, GitLab.com, etc. to allow project importing.
-- Connecting with Google, Twitter, GitHub, etc. to allow OAuth authentication.
-
-We use [the Secure Headers gem][secure_headers] to enable Content
-Security Policy headers in the GitLab Rails app.
-
-Some resources on implementing Content Security Policy:
-
-- [MDN Article on CSP][mdn-csp]
-- [GitHub’s CSP Journey on the GitHub Engineering Blog][github-eng-csp]
-- The Dropbox Engineering Blog's series on CSP: [1][dropbox-csp-1], [2][dropbox-csp-2], [3][dropbox-csp-3], [4][dropbox-csp-4]
-
-### Subresource Integrity (SRI)
-
-Subresource Integrity prevents malicious assets from being provided by a CDN by
-guaranteeing that the asset downloaded is identical to the asset the server
-is expecting.
-
-The Rails app generates a unique hash of the asset, which is used as the
-asset's `integrity` attribute. The browser generates the hash of the asset
-on-load and will reject the asset if the hashes do not match.
-
-All CSS and JavaScript assets should use Subresource Integrity. For implementation details,
-see the documentation for [the Sprockets implementation of SRI][sprockets-sri].
-
-Some resources on implementing Subresource Integrity:
-
-- [MDN Article on SRI][mdn-sri]
-- [Subresource Integrity on the GitHub Engineering Blog][github-eng-sri]
-
--->
-
-### Including external resources
-
-External fonts, CSS, and JavaScript should never be used with the exception of
-Google Analytics and Piwik - and only when the instance has enabled it. Assets
-should always be hosted and served locally from the GitLab instance. Embedded
-resources via `iframes` should never be used except in certain circumstances
-such as with ReCaptcha, which cannot be used without an `iframe`.
-
-### Avoiding inline scripts and styles
-
-In order to protect users from [XSS vulnerabilities][xss], we will disable inline scripts in the future using Content Security Policy.
-
-While inline scripts can be useful, they're also a security concern. If
-user-supplied content is unintentionally left un-sanitized, malicious users can
-inject scripts into the web app.
-
-Inline styles should be avoided in almost all cases, they should only be used
-when no alternatives can be found. This allows reusability of styles as well as
-readability.
-
-## Style guides and linting
-
-See the relevant style guides for our guidelines and for information on linting:
-
-- [SCSS][scss-style-guide]
-- JavaScript - We defer to [AirBnb][airbnb-js-style-guide] on most style-related
-conventions and enforce them with eslint. See [our current .eslintrc][eslintrc]
-for specific rules and patterns.
-
-## Testing
-
-Feature tests need to be written for all new features. Regression tests
-also need to be written for all bug fixes to prevent them from occurring
-again in the future.
-
-See [the Testing Standards and Style Guidelines](testing.md) for more
-information.
-
-### Running frontend tests
-
-`rake karma` runs the frontend-only (JavaScript) tests.
-It consists of two subtasks:
-
-- `rake karma:fixtures` (re-)generates fixtures
-- `rake karma:tests` actually executes the tests
-
-As long as the fixtures don't change, `rake karma:tests` is sufficient
-(and saves you some time).
-
-Please note: Not all of the frontend fixtures are generated. Some are still static
-files. These will not be touched by `rake karma:fixtures`.
-
-## Design Patterns
-
-### Singletons
-
-When exactly one object is needed for a given task, prefer to define it as a
-`class` rather than as an object literal. Prefer also to explicitly restrict
-instantiation, unless flexibility is important (e.g. for testing).
-
-```javascript
-// bad
-
-gl.MyThing = {
- prop1: 'hello',
- method1: () => {}
-};
-
-// good
-
-class MyThing {
- constructor() {
- this.prop1 = 'hello';
- }
- method1() {}
-}
-
-gl.MyThing = new MyThing();
-
-// best
-
-let singleton;
-
-class MyThing {
- constructor() {
- if (!singleton) {
- singleton = this;
- singleton.init();
- }
- return singleton;
- }
-
- init() {
- this.prop1 = 'hello';
- }
-
- method1() {}
-}
-
-gl.MyThing = MyThing;
-
-```
-
-### Manipulating the DOM in a JS Class
-
-When writing a class that needs to manipulate the DOM guarantee a container option is provided.
-This is useful when we need that class to be instantiated more than once in the same page.
-
-Bad:
-```javascript
-class Foo {
- constructor() {
- document.querySelector('.bar');
- }
-}
-new Foo();
-```
-
-Good:
-```javascript
-class Foo {
- constructor(opts) {
- document.querySelector(`${opts.container} .bar`);
- }
-}
-
-new Foo({ container: '.my-element' });
-```
-You can find an example of the above in this [class][container-class-example];
-
-## Supported browsers
-
-For our currently-supported browsers, see our [requirements][requirements].
-
-
-## Gotchas
-
-### Spec errors due to use of ES6 features in `.js` files
-
-If you see very generic JavaScript errors (e.g. `jQuery is undefined`) being
-thrown in Karma, Spinach, or Rspec tests but can't reproduce them manually,
-you may have included `ES6`-style JavaScript in files that don't have the
-`.js.es6` file extension. Either use ES5-friendly JavaScript or rename the file
-you're working in (`git mv <file.js> <file.js.es6>`).
-
-### Spec errors due to use of unsupported JavaScript
-
-Similar errors will be thrown if you're using JavaScript features not yet
-supported by our test runner's version of webkit, whether or not you've updated
-the file extension. Examples of unsupported JavaScript features are:
-
-- Array.from
-- Array.find
-- Array.first
-- Object.assign
-- Async functions
-- Generators
-- Array destructuring
-- For Of
-- Symbol/Symbol.iterator
-- Spread
-
-Until these are polyfilled or transpiled appropriately, they should not be used.
-Please update this list with additional unsupported features or when any of
-these are made usable.
-
-### Spec errors due to JavaScript not enabled
-
-If, as a result of a change you've made, a feature now depends on JavaScript to
-run correctly, you need to make sure a JavaScript web driver is enabled when
-specs are run. If you don't you'll see vague error messages from the spec
-runner, and an explosion of vague console errors in the HTML snapshot.
-
-To enable a JavaScript driver in an `rspec` test, add `js: true` to the
-individual spec or the context block containing multiple specs that need
-JavaScript enabled:
-
-```ruby
-
-# For one spec
-it 'presents information about abuse report', js: true do
- # assertions...
-end
-
-describe "Admin::AbuseReports", js: true do
- it 'presents information about abuse report' do
- # assertions...
- end
- it 'shows buttons for adding to abuse report' do
- # assertions...
- end
-end
-```
-
-In Spinach, the JavaScript driver is enabled differently. In the `*.feature`
-file for the failing spec, add the `@javascript` flag above the Scenario:
-
-```
-@javascript
-Scenario: Developer can approve merge request
- Given I am a "Shop" developer
- And I visit project "Shop" merge requests page
- And merge request 'Bug NS-04' must be approved
- And I click link "Bug NS-04"
- When I click link "Approve"
- Then I should see approved merge request "Bug NS-04"
-
-```
+# Frontend Development Guidelines
-[rails]: http://rubyonrails.org/
-[haml]: http://haml.info/
-[hamlit]: https://github.com/k0kubun/hamlit
-[hamlit-limits]: https://github.com/k0kubun/hamlit/blob/master/REFERENCE.md#limitations
-[scss]: http://sass-lang.com/
-[es6]: https://babeljs.io/
-[sprockets]: https://github.com/rails/sprockets
-[jquery]: https://jquery.com/
-[vue]: http://vuejs.org/
-[vue-docs]: http://vuejs.org/guide/index.html
-[web-page-test]: http://www.webpagetest.org/
-[pagespeed-insights]: https://developers.google.com/speed/pagespeed/insights/
-[google-devtools-profiling]: https://developers.google.com/web/tools/chrome-devtools/profile/?hl=en
-[browser-diet]: https://browserdiet.com/
-[d3]: https://d3js.org/
-[chartjs]: http://www.chartjs.org/
-[page-specific-js-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/13bb9ed77f405c5f6ee4fdbc964ecf635c9a223f/app/views/projects/graphs/_head.html.haml#L6-8
-[chrome-accessibility-developer-tools]: https://github.com/GoogleChrome/accessibility-developer-tools
-[audit-rules]: https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules
-[observatory-cli]: https://github.com/mozilla/http-observatory-cli
-[qualys-ssl]: https://www.ssllabs.com/ssltest/analyze.html
-[secure_headers]: https://github.com/twitter/secureheaders
-[mdn-csp]: https://developer.mozilla.org/en-US/docs/Web/Security/CSP
-[github-eng-csp]: http://githubengineering.com/githubs-csp-journey/
-[dropbox-csp-1]: https://blogs.dropbox.com/tech/2015/09/on-csp-reporting-and-filtering/
-[dropbox-csp-2]: https://blogs.dropbox.com/tech/2015/09/unsafe-inline-and-nonce-deployment/
-[dropbox-csp-3]: https://blogs.dropbox.com/tech/2015/09/csp-the-unexpected-eval/
-[dropbox-csp-4]: https://blogs.dropbox.com/tech/2015/09/csp-third-party-integrations-and-privilege-separation/
-[mdn-sri]: https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity
-[github-eng-sri]: http://githubengineering.com/subresource-integrity/
-[sprockets-sri]: https://github.com/rails/sprockets-rails#sri-support
-[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting
-[scss-style-guide]: scss_styleguide.md
-[requirements]: ../install/requirements.md#supported-web-browsers
-[issue-boards]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/boards
-[environments-table]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/app/assets/javascripts/environments
-[page_specific_javascript]: https://docs.gitlab.com/ce/development/frontend.html#page-specific-javascript
-[component-system]: https://vuejs.org/v2/guide/#Composing-with-Components
-[state-management]: https://vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
-[vue-resource-repo]: https://github.com/pagekit/vue-resource
-[issue-boards-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/boards/services/board_service.js.es6
-[airbnb-js-style-guide]: https://github.com/airbnb/javascript
-[eslintrc]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.eslintrc
-[team-page]: https://about.gitlab.com/team
-[vue-section]: https://docs.gitlab.com/ce/development/frontend.html#how-to-build-a-new-feature-with-vue-js
-[container-class-example]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/mini_pipeline_graph_dropdown.js
+This page has moved [here](fe_guide/index.md).
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index dcd978c4cd3..ec9e4dcc59d 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -42,6 +42,20 @@ To run several tests inside one directory:
If you want to use [Spring](https://github.com/rails/spring) set
`ENABLE_SPRING=1` in your environment.
+## Compile Frontend Assets
+
+You shouldn't ever need to compile frontend assets manually in development, but
+if you ever need to test how the assets get compiled in a production
+environment you can do so with the following command:
+
+```
+RAILS_ENV=production NODE_ENV=production bundle exec rake gitlab:assets:compile
+```
+
+This will compile and minify all JavaScript and CSS assets and copy them along
+with all other frontend assets (images, fonts, etc) into `/public/assets` where
+they can be easily inspected.
+
## Generate API documentation for project services (e.g. Slack)
```
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 5ac7b8dadeb..5bc958f5a96 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -34,16 +34,17 @@ GitLab uses [factory_girl] as a test fixture replacement.
GitLab uses [Karma] to run its [Jasmine] JavaScript specs. They can be run on
the command line via `bundle exec karma`.
-- JavaScript tests live in `spec/javascripts/`, matching the folder structure of
- `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js` has a corresponding
- `spec/javascripts/behaviors/autosize_spec.js` file.
+- JavaScript tests live in `spec/javascripts/`, matching the folder structure
+ of `app/assets/javascripts/`: `app/assets/javascripts/behaviors/autosize.js`
+ has a corresponding `spec/javascripts/behaviors/autosize_spec.js` file.
- Haml fixtures required for JavaScript tests live in
`spec/javascripts/fixtures`. They should contain the bare minimum amount of
markup necessary for the test.
> **Warning:** Keep in mind that a Rails view may change and
invalidate your test, but everything will still pass because your fixture
- doesn't reflect the latest view.
+ doesn't reflect the latest view. Because of this we encourage you to
+ generate fixtures from actual rails views whenever possible.
- Keep in mind that in a CI environment, these tests are run in a headless
browser and you will not have access to certain APIs, such as
@@ -53,6 +54,8 @@ the command line via `bundle exec karma`.
[Karma]: https://github.com/karma-runner/karma
[Jasmine]: https://github.com/jasmine/jasmine
+For more information, see the [frontend testing guide](fe_guide/testing.md).
+
## RSpec
### General Guidelines
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 177e1a9378b..a6b10176450 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -109,7 +109,8 @@ Then select 'Internet Site' and press enter to confirm the hostname.
## 2. Ruby
-**Note:** The current supported Ruby versions are 2.1.x and 2.3.x. 2.3.x is preferred, and support for 2.1.x will be dropped in the future.
+**Note:** The current supported Ruby version is 2.3.x. GitLab 9.0 dropped support
+for Ruby 2.1.x.
The use of Ruby version managers such as [RVM], [rbenv] or [chruby] with GitLab
in production, frequently leads to hard to diagnose problems. For example,
@@ -456,6 +457,36 @@ Make GitLab start on boot:
sudo update-rc.d gitlab defaults 21
+### Install Gitaly
+
+As of GitLab 9.0 Gitaly is an **optional** component. Its
+configuration is expected to change in GitLab 9.1. It is OK to wait
+with setting up Gitaly until you upgrade to GitLab 9.1 or later.
+
+ # Fetch Gitaly source with Git and compile with Go
+ sudo -u git -H bundle exec rake "gitlab:gitaly:install[/home/git/gitaly]" RAILS_ENV=production
+
+ # Restrict Gitaly socket access
+ sudo chmod 0700 /home/git/gitlab/tmp/sockets/private
+ sudo chown git /home/git/gitlab/tmp/sockets/private
+
+ # Configure Gitaly
+ echo 'GITALY_SOCKET_PATH=/home/git/gitlab/tmp/sockets/private/gitaly.socket' | \
+ sudo -u git tee -a /home/git/gitaly/env
+
+ # Enable Gitaly in the init script
+ echo 'gitaly_enabled=true' | sudo tee -a /etc/default/gitlab
+
+Next, edit `/home/git/gitlab/config/gitlab.yml` and make sure `socket_path` in
+the `gitaly:` section is uncommented.
+
+ # <- gitlab.yml indentation starts here
+ gitaly:
+ socket_path: tmp/sockets/private/gitaly.socket
+
+For more information about configuring Gitaly see
+[doc/administration/gitaly](../administration/gitaly).
+
### Setup Logrotate
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
diff --git a/doc/profile/README.md b/doc/profile/README.md
index 6f8359d87fa..54e44d65959 100644
--- a/doc/profile/README.md
+++ b/doc/profile/README.md
@@ -1,4 +1,4 @@
-# Profile Settings
+# Profile settings
-- [Preferences](preferences.md)
-- [Two-factor Authentication (2FA)](two_factor_authentication.md)
+- [Preferences](../user/profile/preferences.md)
+- [Two-factor Authentication (2FA)](../user/profile/account/two_factor_authentication.md)
diff --git a/doc/profile/preferences.md b/doc/profile/preferences.md
index 4f2b00f3dd1..cc16f3afe41 100644
--- a/doc/profile/preferences.md
+++ b/doc/profile/preferences.md
@@ -1,36 +1 @@
-# Profile Preferences
-
-Settings in the **Profile > Preferences** page allow the user to customize
-various aspects of the site to their liking.
-
-## Syntax highlighting theme
-
-_GitLab uses the [rouge ruby library][rouge] for syntax highlighting. For a
-list of supported languages visit the rouge website._
-
-Changing this setting allows the user to customize the theme used when viewing
-syntax highlighted code on the site.
-
-The default is **White**.
-
-## Behavior
-
-### Default Dashboard
-
-For users who have access to a large number of projects but only keep up with a
-select few, the amount of activity on the default Dashboard page can be
-overwhelming.
-
-Changing this setting allows the user to redefine what their default dashboard
-will be. Setting it to **Starred Projects** will make that Dashboard view the
-default when signing in or clicking the application logo in the upper left.
-
-The default is **Your Projects**.
-
-### Default Project view
-
-It allows user to choose what content he or she want to see on project page.
-
-The default is **Readme**.
-
-[rouge]: http://rouge.jneen.net/ "Rouge website"
+This document was moved to [another location](../user/profile/preferences.md).
diff --git a/doc/update/8.13-to-8.14.md b/doc/update/8.13-to-8.14.md
index 327ecb7cdc2..aa1c659717e 100644
--- a/doc/update/8.13-to-8.14.md
+++ b/doc/update/8.13-to-8.14.md
@@ -72,7 +72,7 @@ sudo -u git -H git checkout 8-14-stable-ee
```bash
cd /home/git/gitlab-shell
sudo -u git -H git fetch --all --tags
-sudo -u git -H git checkout v4.0.3
+sudo -u git -H git checkout v4.1.1
```
### 6. Update gitlab-workhorse
diff --git a/doc/update/8.17-to-9.0.md b/doc/update/8.17-to-9.0.md
index b7ba970031c..6308317b1f2 100644
--- a/doc/update/8.17-to-9.0.md
+++ b/doc/update/8.17-to-9.0.md
@@ -25,9 +25,8 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
### 3. Update Ruby
-We will continue supporting Ruby < 2.3 for the time being but we recommend you
-upgrade to Ruby 2.3 if you're running a source installation, as this is the same
-version that ships with our Omnibus package.
+NOTE: GitLab 9.0 only supports Ruby 2.3.x and dropped support for Ruby 2.1.x. Be
+sure to upgrade your interpreter if necessary.
You can check which version you are running with `ruby -v`.
@@ -98,54 +97,32 @@ cd /home/git/gitlab
sudo -u git -H git checkout 9-0-stable-ee
```
-### 6. Install libs, migrations, etc.
+### 6. Update gitlab-shell
```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
-
-# 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
+cd /home/git/gitlab-shell
-# Clean up cache
-sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
+sudo -u git -H git fetch --all --tags
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_SHELL_VERSION)
```
-**MySQL installations**: Run through the `MySQL strings limits` and `Tables and data conversion to utf8mb4` [tasks](../install/database_mysql.md).
-
### 7. Update gitlab-workhorse
Install and compile gitlab-workhorse. This requires
[Go 1.5](https://golang.org/dl) which should already be on your system from
-GitLab 8.1.
+GitLab 8.1. 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
-
-sudo -u git -H bundle exec rake "gitlab:workhorse:install[/home/git/gitlab-workhorse]" RAILS_ENV=production
-```
-
-### 8. Update gitlab-shell
-
-```bash
-cd /home/git/gitlab-shell
+cd /home/git/gitlab-workhorse
sudo -u git -H git fetch --all --tags
-sudo -u git -H git checkout v5.0.0
+sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_WORKHORSE_VERSION)
+sudo -u git -H make
```
-### 9. Update configuration files
+### 8. Update configuration files
#### New configuration options for `gitlab.yml`
@@ -282,14 +259,48 @@ For Ubuntu 16.04.1 LTS:
sudo systemctl daemon-reload
```
-### 10. Start application
+### 9. 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
+
+# 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).
+
+### 10. Optional: install Gitaly
+
+Gitaly is still an optional component of GitLab. If you want to save time
+during your 9.0 upgrade **you can skip this step**.
+
+If you do want to set up Gitaly in GitLab 9.0 then follow [Gitaly section of the installation
+guide](../install/installation.md#install-gitaly).
+
+### 11. Start application
```bash
sudo service gitlab start
sudo service nginx restart
```
-### 11. Check application status
+### 12. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
new file mode 100644
index 00000000000..e5038835027
--- /dev/null
+++ b/doc/user/profile/preferences.md
@@ -0,0 +1,64 @@
+# Profile preferences
+
+A user's profile preferences page allows the user to customize various aspects
+of GitLab to their liking.
+
+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.
+
+## Syntax highlighting theme
+
+>**Note:**
+GitLab uses the [rouge Ruby library][rouge] for syntax highlighting. For a
+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:
+
+- White
+- Dark
+- Solarized light
+- Solarized dark
+- Monokai
+
+## Behavior
+
+The following settings allow you to customize the behavior of GitLab's layout
+and default views of your dashboard and the projects' landing pages.
+
+### Layout width
+
+GitLab can be set up to use different widths depending on your liking. Choose
+between the fixed (max. 1200px) and the fluid (100%) application layout.
+
+### Default dashboard
+
+For users who have access to a large number of projects but only keep up with a
+select few, the amount of activity on the default Dashboard page can be
+overwhelming. Changing this setting allows you to redefine what your default
+dashboard will be.
+
+You have 6 options here that you can use for your default dashboard view:
+
+- Your projects (default)
+- Starred projects
+- Your projects' activity
+- Starred projects' activity
+- Your groups
+- Your [Todos]
+
+### Default project view
+
+The default project view settings allows you to choose what content you want to
+see on a project's landing page.
+
+You can choose between 2 options:
+
+- Show the files and the readme (default)
+- Show the project's activity
+
+[rouge]: http://rouge.jneen.net/ "Rouge website"
+[todos]: ../../workflow/todos.md
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index 7aa9b46081a..f94357abec9 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -36,6 +36,7 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| <kbd>g</kbd> + <kbd>p</kbd> | Go to projects |
| <kbd>g</kbd> + <kbd>i</kbd> | Go to issues |
| <kbd>g</kbd> + <kbd>m</kbd> | Go to merge requests |
+| <kbd>g</kbd> + <kbd>t</kbd> | Go to todos |
## Project
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
index 1762d5bdf95..e55dc2913c3 100644
--- a/features/steps/project/issues/award_emoji.rb
+++ b/features/steps/project/issues/award_emoji.rb
@@ -45,7 +45,7 @@ class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
end
step 'I have new comment with emoji added' do
- expect(page).to have_selector ".emoji[title=':smile:']"
+ expect(page).to have_selector 'gl-emoji[data-name="smile"]'
end
step 'I have award added' do
diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb
index 11fa85ed2fe..071aa2e3eff 100644
--- a/features/steps/shared/diff_note.rb
+++ b/features/steps/shared/diff_note.rb
@@ -196,7 +196,7 @@ module SharedDiffNote
step 'The diff comment preview tab should display rendered Markdown' do
page.within(diff_file_selector) do
find('.js-md-preview-button').click
- expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
+ expect(find('.js-md-preview')).to have_css('gl-emoji', visible: true)
end
end
@@ -210,7 +210,7 @@ module SharedDiffNote
step 'I should see a diff comment with an emoji image' do
page.within("#{diff_file_selector} .note") do
- expect(page).to have_xpath("//img[@alt=':smile:']")
+ expect(page).to have_xpath("//gl-emoji[@data-name='smile']")
end
end
diff --git a/features/steps/shared/markdown.rb b/features/steps/shared/markdown.rb
index a036d9b884f..875d27d9383 100644
--- a/features/steps/shared/markdown.rb
+++ b/features/steps/shared/markdown.rb
@@ -40,7 +40,7 @@ module SharedMarkdown
step 'The Markdown preview tab should display rendered Markdown' do
page.within('.gfm-form') do
find('.js-md-preview-button').click
- expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
+ expect(find('.js-md-preview')).to have_css('gl-emoji', visible: true)
end
end
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index 1870f6bc0c3..fd925e0d447 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -95,7 +95,7 @@ module SharedNote
step 'The comment preview tab should be display rendered Markdown' do
page.within(".js-main-target-form") do
find('.js-md-preview-button').click
- expect(find('.js-md-preview')).to have_css('img.emoji', visible: true)
+ expect(find('.js-md-preview')).to have_css('gl-emoji', visible: true)
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 2d4d5a25221..a4201fe6fed 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -27,7 +27,7 @@ module API
optional :location, type: String, desc: 'The location of the user'
optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
- optional :confirm, type: Boolean, desc: 'Flag indicating the account needs to be confirmed'
+ optional :skip_confirmation, type: Boolean, default: false, desc: 'Flag indicating the account is confirmed'
optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
all_or_none_of :extern_uid, :provider
end
@@ -97,29 +97,10 @@ module API
post do
authenticated_as_admin!
- # Filter out params which are used later
- user_params = declared_params(include_missing: false)
- identity_attrs = user_params.slice(:provider, :extern_uid)
- confirm = user_params.delete(:confirm)
- user = User.new(user_params.except(:extern_uid, :provider, :reset_password))
-
- if user_params.delete(:reset_password)
- user.attributes = {
- force_random_password: true,
- password_expires_at: nil,
- created_by_id: current_user.id
- }
- user.generate_password
- user.generate_reset_token
- end
-
- user.skip_confirmation! unless confirm
-
- if identity_attrs.any?
- user.identities.build(identity_attrs)
- end
+ params = declared_params(include_missing: false)
+ user = ::Users::CreateService.new(current_user, params).execute
- if user.save
+ if user.persisted?
present user, with: Entities::UserPublic
else
conflict!('Email has already been taken') if User.
diff --git a/lib/api/v3/users.rb b/lib/api/v3/users.rb
index 14f54731730..5e18cecc431 100644
--- a/lib/api/v3/users.rb
+++ b/lib/api/v3/users.rb
@@ -9,6 +9,59 @@ module API
end
resource :users, requirements: { uid: /[0-9]*/, id: /[0-9]*/ } do
+ helpers do
+ params :optional_attributes do
+ optional :skype, type: String, desc: 'The Skype username'
+ optional :linkedin, type: String, desc: 'The LinkedIn username'
+ optional :twitter, type: String, desc: 'The Twitter username'
+ optional :website_url, type: String, desc: 'The website of the user'
+ optional :organization, type: String, desc: 'The organization of the user'
+ optional :projects_limit, type: Integer, desc: 'The number of projects a user can create'
+ optional :extern_uid, type: String, desc: 'The external authentication provider UID'
+ optional :provider, type: String, desc: 'The external provider'
+ optional :bio, type: String, desc: 'The biography of the user'
+ optional :location, type: String, desc: 'The location of the user'
+ optional :admin, type: Boolean, desc: 'Flag indicating the user is an administrator'
+ optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups'
+ optional :confirm, type: Boolean, default: true, desc: 'Flag indicating the account needs to be confirmed'
+ optional :external, type: Boolean, desc: 'Flag indicating the user is an external user'
+ all_or_none_of :extern_uid, :provider
+ end
+ end
+
+ desc 'Create a user. Available only for admins.' do
+ success ::API::Entities::UserPublic
+ end
+ params do
+ requires :email, type: String, desc: 'The email of the user'
+ optional :password, type: String, desc: 'The password of the new user'
+ optional :reset_password, type: Boolean, desc: 'Flag indicating the user will be sent a password reset token'
+ at_least_one_of :password, :reset_password
+ requires :name, type: String, desc: 'The name of the user'
+ requires :username, type: String, desc: 'The username of the user'
+ use :optional_attributes
+ end
+ post do
+ authenticated_as_admin!
+
+ params = declared_params(include_missing: false)
+ user = ::Users::CreateService.new(current_user, params.merge!(skip_confirmation: !params[:confirm])).execute
+
+ if user.persisted?
+ present user, with: ::API::Entities::UserPublic
+ else
+ conflict!('Email has already been taken') if User.
+ where(email: user.email).
+ count > 0
+
+ conflict!('Username has already been taken') if User.
+ where(username: user.username).
+ count > 0
+
+ render_validation_error!(user)
+ end
+ end
+
desc 'Get the SSH keys of a specified user. Available only for admins.' do
success ::API::Entities::SSHKey
end
diff --git a/lib/gitlab/ci/build/step.rb b/lib/gitlab/ci/build/step.rb
index 1877429ac46..ee034d9cc56 100644
--- a/lib/gitlab/ci/build/step.rb
+++ b/lib/gitlab/ci/build/step.rb
@@ -7,13 +7,12 @@ module Gitlab
WHEN_ALWAYS = 'always'.freeze
attr_reader :name
- attr_writer :script
- attr_accessor :timeout, :when, :allow_failure
+ attr_accessor :script, :timeout, :when, :allow_failure
class << self
def from_commands(job)
self.new(:script).tap do |step|
- step.script = job.commands
+ step.script = job.commands.split("\n")
step.timeout = job.timeout
step.when = WHEN_ON_SUCCESS
end
@@ -36,10 +35,6 @@ module Gitlab
@name = name
@allow_failure = false
end
-
- def script
- @script.split("\n")
- end
end
end
end
diff --git a/lib/gitlab/ee_compat_check.rb b/lib/gitlab/ee_compat_check.rb
index e0fdf3f3d64..0829c1c318e 100644
--- a/lib/gitlab/ee_compat_check.rb
+++ b/lib/gitlab/ee_compat_check.rb
@@ -5,35 +5,44 @@ module Gitlab
CE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ce.git'.freeze
EE_REPO = 'https://gitlab.com/gitlab-org/gitlab-ee.git'.freeze
CHECK_DIR = Rails.root.join('ee_compat_check')
- MAX_FETCH_DEPTH = 500
IGNORED_FILES_REGEX = /(VERSION|CHANGELOG\.md:\d+)/.freeze
-
- attr_reader :repo_dir, :patches_dir, :ce_repo, :ce_branch
+ PLEASE_READ_THIS_BANNER = %Q{
+ ============================================================
+ ===================== PLEASE READ THIS =====================
+ ============================================================
+ }.freeze
+ THANKS_FOR_READING_BANNER = %Q{
+ ============================================================
+ ==================== THANKS FOR READING ====================
+ ============================================================\n
+ }.freeze
+
+ attr_reader :ee_repo_dir, :patches_dir, :ce_repo, :ce_branch, :ee_branch_found
+ attr_reader :failed_files
def initialize(branch:, ce_repo: CE_REPO)
- @repo_dir = CHECK_DIR.join('repo')
+ @ee_repo_dir = CHECK_DIR.join('ee-repo')
@patches_dir = CHECK_DIR.join('patches')
@ce_branch = branch
@ce_repo = ce_repo
end
def check
- ensure_ee_repo
ensure_patches_dir
-
generate_patch(ce_branch, ce_patch_full_path)
- Dir.chdir(repo_dir) do
- step("In the #{repo_dir} directory")
+ ensure_ee_repo
+ Dir.chdir(ee_repo_dir) do
+ step("In the #{ee_repo_dir} directory")
status = catch(:halt_check) do
ce_branch_compat_check!
- delete_ee_branch_locally!
+ delete_ee_branches_locally!
ee_branch_presence_check!
ee_branch_compat_check!
end
- delete_ee_branch_locally!
+ delete_ee_branches_locally!
if status.nil?
true
@@ -46,11 +55,13 @@ module Gitlab
private
def ensure_ee_repo
- if Dir.exist?(repo_dir)
- step("#{repo_dir} already exists")
+ if Dir.exist?(ee_repo_dir)
+ step("#{ee_repo_dir} already exists")
else
- cmd = %W[git clone --branch master --single-branch --depth 200 #{EE_REPO} #{repo_dir}]
- step("Cloning #{EE_REPO} into #{repo_dir}", cmd)
+ step(
+ "Cloning #{EE_REPO} into #{ee_repo_dir}",
+ %W[git clone --branch master --single-branch --depth=200 #{EE_REPO} #{ee_repo_dir}]
+ )
end
end
@@ -61,23 +72,18 @@ module Gitlab
def generate_patch(branch, patch_path)
FileUtils.rm(patch_path, force: true)
- depth = 0
- loop do
- depth += 50
- cmd = %W[git fetch --depth #{depth} origin --prune +refs/heads/master:refs/remotes/origin/master]
- Gitlab::Popen.popen(cmd)
- _, status = Gitlab::Popen.popen(%w[git merge-base FETCH_HEAD HEAD])
+ find_merge_base_with_master(branch: branch)
- raise "#{branch} is too far behind master, please rebase it!" if depth >= MAX_FETCH_DEPTH
- break if status.zero?
- end
+ step(
+ "Generating the patch against origin/master in #{patch_path}",
+ %w[git format-patch origin/master --stdout]
+ ) do |output, status|
+ throw(:halt_check, :ko) unless status.zero?
- step("Generating the patch against master in #{patch_path}")
- output, status = Gitlab::Popen.popen(%w[git format-patch FETCH_HEAD --stdout])
- throw(:halt_check, :ko) unless status.zero?
+ File.write(patch_path, output)
- File.write(patch_path, output)
- throw(:halt_check, :ko) unless File.exist?(patch_path)
+ throw(:halt_check, :ko) unless File.exist?(patch_path)
+ end
end
def ce_branch_compat_check!
@@ -88,9 +94,17 @@ module Gitlab
end
def ee_branch_presence_check!
- status = step("Fetching origin/#{ee_branch}", %W[git fetch origin #{ee_branch}])
+ _, status = step("Fetching origin/#{ee_branch_prefix}", %W[git fetch origin #{ee_branch_prefix}])
- unless status.zero?
+ if status.zero?
+ @ee_branch_found = ee_branch_prefix
+ else
+ _, status = step("Fetching origin/#{ee_branch_suffix}", %W[git fetch origin #{ee_branch_suffix}])
+ end
+
+ if status.zero?
+ @ee_branch_found = ee_branch_suffix
+ else
puts
puts ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg
@@ -99,9 +113,9 @@ module Gitlab
end
def ee_branch_compat_check!
- step("Checking out origin/#{ee_branch}", %W[git checkout -b #{ee_branch} FETCH_HEAD])
+ step("Checking out origin/#{ee_branch_found}", %W[git checkout -b #{ee_branch_found} FETCH_HEAD])
- generate_patch(ee_branch, ee_patch_full_path)
+ generate_patch(ee_branch_found, ee_patch_full_path)
unless check_patch(ee_patch_full_path).zero?
puts
@@ -116,36 +130,72 @@ module Gitlab
def check_patch(patch_path)
step("Checking out master", %w[git checkout master])
- step("Reseting to latest master", %w[git reset --hard origin/master])
-
- step("Checking if #{patch_path} applies cleanly to EE/master")
- output, status = Gitlab::Popen.popen(%W[git apply --check --3way #{patch_path}])
-
- unless status.zero?
- failed_files = output.lines.reduce([]) do |memo, line|
- if line.start_with?('error: patch failed:')
- file = line.sub(/\Aerror: patch failed: /, '')
- memo << file unless file =~ IGNORED_FILES_REGEX
+ step("Resetting to latest master", %w[git reset --hard origin/master])
+ step(
+ "Checking if #{patch_path} applies cleanly to EE/master",
+ %W[git apply --check --3way #{patch_path}]
+ ) do |output, status|
+ unless status.zero?
+ @failed_files = output.lines.reduce([]) do |memo, line|
+ if line.start_with?('error: patch failed:')
+ file = line.sub(/\Aerror: patch failed: /, '')
+ memo << file unless file =~ IGNORED_FILES_REGEX
+ end
+ memo
end
- memo
+
+ status = 0 if failed_files.empty?
end
- if failed_files.empty?
- status = 0
- else
- puts "\nConflicting files:"
- failed_files.each do |file|
- puts " - #{file}"
- end
+ status
+ end
+ end
+
+ def delete_ee_branches_locally!
+ command(%w[git checkout master])
+ command(%W[git branch --delete --force #{ee_branch_prefix}])
+ command(%W[git branch --delete --force #{ee_branch_suffix}])
+ end
+
+ def merge_base_found?
+ step(
+ "Finding merge base with master",
+ %w[git merge-base origin/master HEAD]
+ ) do |output, status|
+ if status.zero?
+ puts "Merge base was found: #{output}"
+ true
end
end
+ end
+
+ def find_merge_base_with_master(branch:)
+ return if merge_base_found?
+
+ # Start with (Math.exp(3).to_i = 20) until (Math.exp(6).to_i = 403)
+ # In total we go (20 + 54 + 148 + 403 = 625) commits deeper
+ depth = 20
+ success =
+ (3..6).any? do |factor|
+ depth += Math.exp(factor).to_i
+ # Repository is initially cloned with a depth of 20 so we need to fetch
+ # deeper in the case the branch has more than 20 commits on top of master
+ fetch(branch: branch, depth: depth)
+ fetch(branch: 'master', depth: depth)
+
+ merge_base_found?
+ end
- status
+ raise "\n#{branch} is too far behind master, please rebase it!\n" unless success
end
- def delete_ee_branch_locally!
- command(%w[git checkout master])
- step("Deleting the local #{ee_branch} branch", %W[git branch -D #{ee_branch}])
+ def fetch(branch:, depth:)
+ step(
+ "Fetching deeper...",
+ %W[git fetch --depth=#{depth} --prune origin +refs/heads/#{branch}:refs/remotes/origin/#{branch}]
+ ) do |output, status|
+ raise "Fetch failed: #{output}" unless status.zero?
+ end
end
def ce_patch_name
@@ -156,8 +206,12 @@ module Gitlab
@ce_patch_full_path ||= patches_dir.join(ce_patch_name)
end
- def ee_branch
- @ee_branch ||= "#{ce_branch}-ee"
+ def ee_branch_suffix
+ @ee_branch_suffix ||= "#{ce_branch}-ee"
+ end
+
+ def ee_branch_prefix
+ @ee_branch_prefix ||= "ee-#{ce_branch}"
end
def ee_patch_name
@@ -178,98 +232,125 @@ module Gitlab
if cmd
start = Time.now
puts "\n$ #{cmd.join(' ')}"
- status = command(cmd)
- puts "\nFinished in #{Time.now - start} seconds"
- status
+
+ output, status = command(cmd)
+ puts "\n==> Finished in #{Time.now - start} seconds"
+
+ if block_given?
+ yield(output, status)
+ else
+ [output, status]
+ end
end
end
def command(cmd)
- output, status = Gitlab::Popen.popen(cmd)
- puts output
-
- status
+ Gitlab::Popen.popen(cmd)
end
def applies_cleanly_msg(branch)
- <<-MSG.strip_heredoc
- =================================================================
+ %Q{
+ #{PLEASE_READ_THIS_BANNER}
🎉 Congratulations!! 🎉
- The #{branch} branch applies cleanly to EE/master!
+ The `#{branch}` branch applies cleanly to EE/master!
- Much ❤️!!
- =================================================================\n
- MSG
+ Much ❤️! For more information, see
+ https://docs.gitlab.com/ce/development/limit_ee_conflicts.html#check-the-rake-ee_compat_check-in-your-merge-requests
+ #{THANKS_FOR_READING_BANNER}
+ }
end
def ce_branch_doesnt_apply_cleanly_and_no_ee_branch_msg
- <<-MSG.strip_heredoc
- =================================================================
+ %Q{
+ #{PLEASE_READ_THIS_BANNER}
💥 Oh no! 💥
- The #{ce_branch} branch does not apply cleanly to the current
- EE/master, and no #{ee_branch} branch was found in the EE repository.
+ The `#{ce_branch}` branch does not apply cleanly to the current
+ EE/master, and no `#{ee_branch_prefix}` or `#{ee_branch_suffix}` branch
+ was found in the EE repository.
- Please create a #{ee_branch} branch that includes changes from
- #{ce_branch} but also specific changes than can be applied cleanly
- to EE/master.
+ #{conflicting_files_msg}
+
+ We advise you to create a `#{ee_branch_prefix}` or `#{ee_branch_suffix}`
+ branch that includes changes from `#{ce_branch}` but also specific changes
+ than can be applied cleanly to EE/master. In some cases, the conflicts
+ are trivial and you can ignore the warning from this job. As always,
+ use your best judgment!
There are different ways to create such branch:
- 1. Create a new branch based on the CE branch and rebase it on top of EE/master
+ 1. Create a new branch from master and cherry-pick your CE commits
# In the EE repo
- $ git fetch #{ce_repo} #{ce_branch}
- $ git checkout -b #{ee_branch} FETCH_HEAD
-
- # You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE" commit
- # before rebasing to limit the conflicts-resolving steps during the rebase
$ git fetch origin
- $ git rebase origin/master
+ $ git checkout -b #{ee_branch_prefix} origin/master
+ $ git fetch #{ce_repo} #{ce_branch}
+ $ git cherry-pick SHA # Repeat for all the commits you want to pick
- At this point you will likely have conflicts.
- Solve them, and continue/finish the rebase.
+ You can squash the `#{ce_branch}` commits into a single "Port of #{ce_branch} to EE" commit.
- You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE".
+ 2. Apply your branch's patch to EE
- 2. Create a new branch from master and cherry-pick your CE commits
+ # In the CE repo
+ $ git fetch origin master
+ $ git format-patch origin/master --stdout > #{ce_branch}.patch
# In the EE repo
- $ git fetch origin
- $ git checkout -b #{ee_branch} origin/master
- $ git fetch #{ce_repo} #{ce_branch}
- $ git cherry-pick SHA # Repeat for all the commits you want to pick
+ $ git fetch origin master
+ $ git checkout -b #{ee_branch_prefix} origin/master
+ $ git apply --3way path/to/#{ce_branch}.patch
+
+ At this point you might have conflicts such as:
- You can squash the #{ce_branch} commits into a single "Port of #{ce_branch} to EE" commit.
+ error: patch failed: lib/gitlab/ee_compat_check.rb:5
+ Falling back to three-way merge...
+ Applied patch to 'lib/gitlab/ee_compat_check.rb' with conflicts.
+ U lib/gitlab/ee_compat_check.rb
- Don't forget to push your branch to #{EE_REPO}:
+ Resolve them, stage the changes and commit them.
+
+ ⚠️ Don't forget to push your branch to gitlab-ee:
# In the EE repo
- $ git push origin #{ee_branch}
+ $ git push origin #{ee_branch_prefix}
+
+ ⚠️ Also, don't forget to create a new merge request on gitlab-ce and
+ cross-link it with the CE merge request.
- You can then retry this failed build, and hopefully it should pass.
+ Once this is done, you can retry this failed build, and it should pass.
- Stay 💪 !
- =================================================================\n
- MSG
+ Stay 💪 ! For more information, see
+ https://docs.gitlab.com/ce/development/limit_ee_conflicts.html#check-the-rake-ee_compat_check-in-your-merge-requests
+ #{THANKS_FOR_READING_BANNER}
+ }
end
def ee_branch_doesnt_apply_cleanly_msg
- <<-MSG.strip_heredoc
- =================================================================
+ %Q{
+ #{PLEASE_READ_THIS_BANNER}
💥 Oh no! 💥
- The #{ce_branch} does not apply cleanly to the current
- EE/master, and even though a #{ee_branch} branch exists in the EE
- repository, it does not apply cleanly either to EE/master!
+ The `#{ce_branch}` does not apply cleanly to the current EE/master, and
+ even though a `#{ee_branch_found}` branch
+ exists in the EE repository, it does not apply cleanly either to
+ EE/master!
+
+ #{conflicting_files_msg}
- Please update the #{ee_branch}, push it again to #{EE_REPO}, and
+ Please update the `#{ee_branch_found}`, push it again to gitlab-ee, and
retry this build.
- Stay 💪 !
- =================================================================\n
- MSG
+ Stay 💪 ! For more information, see
+ https://docs.gitlab.com/ce/development/limit_ee_conflicts.html#check-the-rake-ee_compat_check-in-your-merge-requests
+ #{THANKS_FOR_READING_BANNER}
+ }
+ end
+
+ def conflicting_files_msg
+ failed_files.reduce("The conflicts detected were as follows:\n") do |memo, file|
+ memo << "\n - #{file}"
+ end
end
end
end
diff --git a/lib/gitlab/etag_caching/store.rb b/lib/gitlab/etag_caching/store.rb
index 9532e432f78..0039fc01c8f 100644
--- a/lib/gitlab/etag_caching/store.rb
+++ b/lib/gitlab/etag_caching/store.rb
@@ -1,7 +1,7 @@
module Gitlab
module EtagCaching
class Store
- EXPIRY_TIME = 10.minutes
+ EXPIRY_TIME = 20.minutes
REDIS_NAMESPACE = 'etag:'.freeze
def get(key)
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 5534d4af439..1ce47ef2b05 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -2,6 +2,8 @@ require 'gitaly'
module Gitlab
module GitalyClient
+ SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'.freeze
+
def self.gitaly_address
if Gitlab.config.gitaly.socket_path
"unix://#{Gitlab.config.gitaly.socket_path}"
@@ -39,5 +41,10 @@ module Gitlab
yield is_enabled
end
end
+
+ def self.expected_server_version
+ path = Rails.root.join(SERVER_VERSION_FILE)
+ path.read.chomp
+ end
end
end
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index fcf51b7fc5b..f98481c6d3a 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -147,10 +147,8 @@ module Gitlab
end
def build_new_user
- user = ::User.new(user_attributes)
- user.skip_confirmation!
- user.identities.new(extern_uid: auth_hash.uid, provider: auth_hash.provider)
- user
+ user_params = user_attributes.merge(extern_uid: auth_hash.uid, provider: auth_hash.provider, skip_confirmation: true)
+ Users::CreateService.new(nil, user_params).build
end
def user_attributes
diff --git a/lib/gitlab/testing/request_blocker_middleware.rb b/lib/gitlab/testing/request_blocker_middleware.rb
new file mode 100644
index 00000000000..aa67fa08577
--- /dev/null
+++ b/lib/gitlab/testing/request_blocker_middleware.rb
@@ -0,0 +1,61 @@
+# rubocop:disable Style/ClassVars
+
+# This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
+# Rack middleware that keeps track of the number of active requests and can block new requests.
+module Gitlab
+ module Testing
+ class RequestBlockerMiddleware
+ @@num_active_requests = Concurrent::AtomicFixnum.new(0)
+ @@block_requests = Concurrent::AtomicBoolean.new(false)
+
+ # Returns the number of requests the server is currently processing.
+ def self.num_active_requests
+ @@num_active_requests.value
+ end
+
+ # Prevents the server from accepting new requests. Any new requests will return an HTTP
+ # 503 status.
+ def self.block_requests!
+ @@block_requests.value = true
+ end
+
+ # Allows the server to accept requests again.
+ def self.allow_requests!
+ @@block_requests.value = false
+ end
+
+ def initialize(app)
+ @app = app
+ end
+
+ def call(env)
+ increment_active_requests
+ if block_requests?
+ block_request(env)
+ else
+ @app.call(env)
+ end
+ ensure
+ decrement_active_requests
+ end
+
+ private
+
+ def block_requests?
+ @@block_requests.true?
+ end
+
+ def block_request(env)
+ [503, {}, []]
+ end
+
+ def increment_active_requests
+ @@num_active_requests.increment
+ end
+
+ def decrement_active_requests
+ @@num_active_requests.decrement
+ end
+ end
+ end
+end
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index 5fd7f0f98bd..09e121e5120 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -48,6 +48,10 @@ gitlab_pages_pid_path="$pid_path/gitlab-pages.pid"
gitlab_pages_options="-pages-domain example.com -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090"
gitlab_pages_log="$app_root/log/gitlab-pages.log"
shell_path="/bin/bash"
+gitaly_enabled=false
+gitaly_dir=$(cd $app_root/../gitaly 2> /dev/null && pwd)
+gitaly_pid_path="$pid_path/gitaly.pid"
+gitaly_log="$app_root/log/gitaly.log"
# Read configuration variable file if it is present
test -f /etc/default/gitlab && . /etc/default/gitlab
@@ -101,13 +105,20 @@ check_pids(){
gppid=0
fi
fi
+ if [ "$gitaly_enabled" = true ]; then
+ if [ -f "$gitaly_pid_path" ]; then
+ gapid=$(cat "$gitaly_pid_path")
+ else
+ gapid=0
+ fi
+ fi
}
## Called when we have started the two processes and are waiting for their pid files.
wait_for_pids(){
# We are sleeping a bit here mostly because sidekiq is slow at writing its pid
i=0;
- while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; } || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; }; do
+ while [ ! -f $web_server_pid_path ] || [ ! -f $sidekiq_pid_path ] || [ ! -f $gitlab_workhorse_pid_path ] || { [ "$mail_room_enabled" = true ] && [ ! -f $mail_room_pid_path ]; } || { [ "$gitlab_pages_enabled" = true ] && [ ! -f $gitlab_pages_pid_path ]; } || { [ "$gitaly_enabled" = true ] && [ ! -f $gitaly_pid_path ]; }; do
sleep 0.1;
i=$((i+1))
if [ $((i%10)) = 0 ]; then
@@ -164,7 +175,15 @@ check_status(){
gitlab_pages_status="-1"
fi
fi
- if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; }; then
+ if [ "$gitaly_enabled" = true ]; then
+ if [ $gapid -ne 0 ]; then
+ kill -0 "$gapid" 2>/dev/null
+ gitaly_status="$?"
+ else
+ gitaly_status="-1"
+ fi
+ fi
+ if [ $web_status = 0 ] && [ $sidekiq_status = 0 ] && [ $gitlab_workhorse_status = 0 ] && { [ "$mail_room_enabled" != true ] || [ $mail_room_status = 0 ]; } && { [ "$gitlab_pages_enabled" != true ] || [ $gitlab_pages_status = 0 ]; } && { [ "$gitaly_enabled" != true ] || [ $gitaly_status = 0 ]; }; then
gitlab_status=0
else
# http://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
@@ -213,12 +232,19 @@ check_stale_pids(){
exit 1
fi
fi
+ if [ "$gitaly_enabled" = true ] && [ "$gapid" != "0" ] && [ "$gitaly_status" != "0" ]; then
+ echo "Removing stale Gitaly pid. This is most likely caused by Gitaly crashing the last time it ran."
+ if ! rm "$gitaly_pid_path"; then
+ echo "Unable to remove stale pid, exiting"
+ exit 1
+ fi
+ fi
}
## If no parts of the service is running, bail out.
exit_if_not_running(){
check_stale_pids
- if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
+ if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" != "0" ]; }; then
echo "GitLab is not running."
exit
fi
@@ -243,6 +269,9 @@ start_gitlab() {
if [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" != "0" ]; then
echo "Starting GitLab Pages"
fi
+ if [ "$gitaly_enabled" = true ] && [ "$gitaly_status" != "0" ]; then
+ echo "Starting Gitaly"
+ fi
# Then check if the service is running. If it is: don't start again.
if [ "$web_status" = "0" ]; then
@@ -292,6 +321,16 @@ start_gitlab() {
fi
fi
+ if [ "$gitaly_enabled" = true ]; then
+ if [ "$gitaly_status" = "0" ]; then
+ echo "Gitaly is already running with pid $gapid, not restarting"
+ else
+ $app_root/bin/daemon_with_pidfile $gitaly_pid_path \
+ $app_root/bin/with_env $gitaly_dir/env \
+ $gitaly_dir/gitaly >> $gitaly_log 2>&1 &
+ fi
+ fi
+
# Wait for the pids to be planted
wait_for_pids
# Finally check the status to tell wether or not GitLab is running
@@ -322,13 +361,17 @@ stop_gitlab() {
echo "Shutting down gitlab-pages"
kill -- $(cat $gitlab_pages_pid_path)
fi
+ if [ "$gitaly_status" = "0" ]; then
+ echo "Shutting down Gitaly"
+ kill -- $(cat $gitaly_pid_path)
+ fi
# If something needs to be stopped, lets wait for it to stop. Never use SIGKILL in a script.
- while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; do
+ while [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse_status" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; } || { [ "$gitaly_enabled" = true ] && [ "$gitaly_status" = "0" ]; }; do
sleep 1
check_status
printf "."
- if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
+ if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" != "0" ]; }; then
printf "\n"
break
fi
@@ -343,6 +386,7 @@ stop_gitlab() {
rm "$mail_room_pid_path" 2>/dev/null
fi
rm -f "$gitlab_pages_pid_path"
+ rm -f "$gitaly_pid_path"
print_status
}
@@ -350,7 +394,7 @@ stop_gitlab() {
## Prints the status of GitLab and its components.
print_status() {
check_status
- if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; }; then
+ if [ "$web_status" != "0" ] && [ "$sidekiq_status" != "0" ] && [ "$gitlab_workhorse_status" != "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" != "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" != "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" != "0" ]; }; then
echo "GitLab is not running."
return
fi
@@ -383,7 +427,14 @@ print_status() {
printf "The GitLab Pages is \033[31mnot running\033[0m.\n"
fi
fi
- if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; }; then
+ if [ "$gitaly_enabled" = true ]; then
+ if [ "$gitaly_status" = "0" ]; then
+ echo "Gitaly with pid $gapid is running."
+ else
+ printf "Gitaly is \033[31mnot running\033[0m.\n"
+ fi
+ fi
+ if [ "$web_status" = "0" ] && [ "$sidekiq_status" = "0" ] && [ "$gitlab_workhorse_status" = "0" ] && { [ "$mail_room_enabled" != true ] || [ "$mail_room_status" = "0" ]; } && { [ "$gitlab_pages_enabled" != true ] || [ "$gitlab_pages_status" = "0" ]; } && { [ "$gitaly_enabled" != true ] || [ "$gitaly_status" = "0" ]; }; then
printf "GitLab and all its components are \033[32mup and running\033[0m.\n"
fi
}
@@ -414,7 +465,7 @@ reload_gitlab(){
## Restarts Sidekiq and Unicorn.
restart_gitlab(){
check_status
- if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; }; then
+ if [ "$web_status" = "0" ] || [ "$sidekiq_status" = "0" ] || [ "$gitlab_workhorse" = "0" ] || { [ "$mail_room_enabled" = true ] && [ "$mail_room_status" = "0" ]; } || { [ "$gitlab_pages_enabled" = true ] && [ "$gitlab_pages_status" = "0" ]; } || { [ "$gitaly_enabled" = true ] && [ "$gitaly_status" = "0" ]; }; then
stop_gitlab
fi
start_gitlab
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index f6642527639..9472c3c992f 100644
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -84,3 +84,7 @@ mail_room_pid_path="$pid_path/mail_room.pid"
# shell other than "bash"
# The default is "/bin/bash"
shell_path="/bin/bash"
+
+# This variable controls whether the init script starts/stops Gitaly
+gitaly_enabled=false
+gitaly_log="$app_root/log/gitaly.log"
diff --git a/lib/tasks/gitlab/assets.rake b/lib/tasks/gitlab/assets.rake
index 098f9851b45..003d57adbbd 100644
--- a/lib/tasks/gitlab/assets.rake
+++ b/lib/tasks/gitlab/assets.rake
@@ -3,16 +3,16 @@ namespace :gitlab do
desc 'GitLab | Assets | Compile all frontend assets'
task compile: [
'yarn:check',
- 'assets:precompile',
+ 'rake:assets:precompile',
'webpack:compile',
- 'gitlab:assets:fix_urls'
+ 'fix_urls'
]
desc 'GitLab | Assets | Clean up old compiled frontend assets'
- task clean: ['assets:clean']
+ task clean: ['rake:assets:clean']
desc 'GitLab | Assets | Remove all compiled frontend assets'
- task purge: ['assets:clobber']
+ task purge: ['rake:assets:clobber']
desc 'GitLab | Assets | Uninstall frontend dependencies'
task purge_modules: ['yarn:clobber']
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
new file mode 100644
index 00000000000..c288e17ac8d
--- /dev/null
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -0,0 +1,23 @@
+namespace :gitlab do
+ namespace :gitaly do
+ desc "GitLab | Install or upgrade gitaly"
+ task :install, [:dir] => :environment do |t, args|
+ warn_user_is_not_gitlab
+ unless args.dir.present?
+ abort %(Please specify the directory where you want to install gitaly:\n rake "gitlab:gitaly:install[/home/git/gitaly]")
+ end
+
+ tag = "v#{Gitlab::GitalyClient.expected_server_version}"
+ repo = 'https://gitlab.com/gitlab-org/gitaly.git'
+
+ checkout_or_clone_tag(tag: tag, repo: repo, target_dir: args.dir)
+
+ _, status = Gitlab::Popen.popen(%w[which gmake])
+ command = status.zero? ? 'gmake' : 'make'
+
+ Dir.chdir(args.dir) do
+ run_command!([command])
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/tasks/gitlab/task_helpers.rb
index bb755ae689b..cdba2262bc2 100644
--- a/lib/tasks/gitlab/task_helpers.rb
+++ b/lib/tasks/gitlab/task_helpers.rb
@@ -81,7 +81,7 @@ module Gitlab
def run_command!(command)
output, status = Gitlab::Popen.popen(command)
- raise Gitlab::TaskFailedError unless status.zero?
+ raise Gitlab::TaskFailedError.new(output) unless status.zero?
output
end
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index f5caca3ddbf..8938bc515f5 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -3,10 +3,12 @@ require Rails.root.join('lib/gitlab/database/migration_helpers')
require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes')
require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes')
require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes')
+require Rails.root.join('db/migrate/20170317203554_index_routes_path_for_like')
desc 'GitLab | Sets up PostgreSQL'
task setup_postgresql: :environment do
NamespacesProjectsPathLowerIndexes.new.up
AddUsersLowerUsernameEmailIndexes.new.up
AddLowerPathIndexToRoutes.new.up
+ IndexRoutesPathForLike.new.up
end
diff --git a/lib/tasks/tokens.rake b/lib/tasks/tokens.rake
new file mode 100644
index 00000000000..95735f43802
--- /dev/null
+++ b/lib/tasks/tokens.rake
@@ -0,0 +1,38 @@
+require_relative '../../app/models/concerns/token_authenticatable.rb'
+
+namespace :tokens do
+ desc "Reset all GitLab user auth tokens"
+ task reset_all_auth: :environment do
+ reset_all_users_token(:reset_authentication_token!)
+ end
+
+ desc "Reset all GitLab email tokens"
+ task reset_all_email: :environment do
+ reset_all_users_token(:reset_incoming_email_token!)
+ end
+
+ def reset_all_users_token(reset_token_method)
+ TmpUser.find_in_batches do |batch|
+ puts "Processing batch starting with user ID: #{batch.first.id}"
+ STDOUT.flush
+
+ batch.each(&reset_token_method)
+ end
+ end
+end
+
+class TmpUser < ActiveRecord::Base
+ include TokenAuthenticatable
+
+ self.table_name = 'users'
+
+ def reset_authentication_token!
+ write_new_token(:authentication_token)
+ save!(validate: false)
+ end
+
+ def reset_incoming_email_token!
+ write_new_token(:incoming_email_token)
+ save!(validate: false)
+ end
+end
diff --git a/package.json b/package.json
index b3d038bd3d1..7b6c4556e2c 100644
--- a/package.json
+++ b/package.json
@@ -35,7 +35,8 @@
"stats-webpack-plugin": "^0.4.3",
"timeago.js": "^2.0.5",
"underscore": "^1.8.3",
- "vue": "^2.1.10",
+ "visibilityjs": "^1.2.4",
+ "vue": "^2.2.4",
"vue-resource": "^0.9.3",
"webpack": "^2.2.1",
"webpack-bundle-analyzer": "^2.3.0"
diff --git a/spec/controllers/import/bitbucket_controller_spec.rb b/spec/controllers/import/bitbucket_controller_spec.rb
index fa4cc0ebbe0..51f23e4eeb9 100644
--- a/spec/controllers/import/bitbucket_controller_spec.rb
+++ b/spec/controllers/import/bitbucket_controller_spec.rb
@@ -112,6 +112,17 @@ describe Import::BitbucketController do
post :create, format: :js
end
end
+
+ context 'when the Bitbucket user is unauthorized' do
+ render_views
+
+ it 'returns unauthorized' do
+ allow(controller).to receive(:current_user).and_return(user)
+ allow(user).to receive(:can?).and_return(false)
+
+ post :create, format: :js
+ end
+ end
end
context "when the repository owner is not the Bitbucket user" do
diff --git a/spec/controllers/projects/builds_controller_specs.rb b/spec/controllers/projects/builds_controller_specs.rb
new file mode 100644
index 00000000000..d501f7b3155
--- /dev/null
+++ b/spec/controllers/projects/builds_controller_specs.rb
@@ -0,0 +1,47 @@
+require 'spec_helper'
+
+describe Projects::BuildsController do
+ include ApiHelpers
+
+ let(:project) { create(:empty_project, :public) }
+
+ describe 'GET trace.json' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:user) { create(:user) }
+
+ context 'when user is logged in as developer' do
+ before do
+ project.add_developer(user)
+ sign_in(user)
+ get_trace
+ end
+
+ it 'traces build log' do
+ expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
+ end
+ end
+
+ context 'when user is logged in as non member' do
+ before do
+ sign_in(user)
+ get_trace
+ end
+
+ it 'traces build log' do
+ expect(response).to have_http_status(:ok)
+ expect(json_response['id']).to eq build.id
+ expect(json_response['status']).to eq build.status
+ end
+ end
+
+ def get_trace
+ get :trace, namespace_id: project.namespace,
+ project_id: project,
+ id: build.id,
+ format: :json
+ end
+ end
+end
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index 57a921e3676..734966d50b2 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -90,6 +90,7 @@ describe Projects::IssuesController do
it 'redirects to signin if not logged in' do
get :new, namespace_id: project.namespace, project_id: project
+ expect(flash[:notice]).to eq 'Please sign in to create the new issue.'
expect(response).to redirect_to(new_user_session_path)
end
@@ -241,10 +242,27 @@ describe Projects::IssuesController do
expect(spam_logs.first.recaptcha_verified).to be_falsey
end
- it 'renders verify template' do
- update_spam_issue
+ context 'as HTML' do
+ it 'renders verify template' do
+ update_spam_issue
+
+ expect(response).to render_template(:verify)
+ end
+ end
+
+ context 'as JSON' do
+ before do
+ update_issue({ title: 'Spam Title', description: 'Spam lives here' }, format: :json)
+ end
+
+ it 'renders json errors' do
+ expect(json_response)
+ .to eql("errors" => ["Your issue has been recognized as spam. Please, change the content or solve the reCAPTCHA to proceed."])
+ end
- expect(response).to render_template(:verify)
+ it 'returns 422 status' do
+ expect(response).to have_http_status(422)
+ end
end
end
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index 8cc216445eb..902911071c4 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -30,6 +30,15 @@ describe RegistrationsController do
expect(subject.current_user).to be_nil
end
end
+
+ context 'when signup_enabled? is false' do
+ it 'redirects to sign_in' do
+ allow_any_instance_of(ApplicationSetting).to receive(:signup_enabled?).and_return(false)
+
+ expect { post(:create, user_params) }.not_to change(User, :count)
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
end
context 'when reCAPTCHA is enabled' do
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 6b0d084614b..f78086211f7 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -172,7 +172,7 @@ FactoryGirl.define do
{
image: 'ruby:2.1',
services: ['postgres'],
- after_script: "ls\ndate",
+ after_script: %w(ls date),
artifacts: {
name: 'artifacts_file',
untracked: false,
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 21487541507..ae0bbbd6aeb 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -4,7 +4,6 @@ FactoryGirl.define do
author
association :source_project, :repository, factory: :project
target_project { source_project }
- project { target_project }
# $ git log --pretty=oneline feature..master
# 5937ac0a7beb003549fc5fd26fc247adbce4a52e Add submodule from gitlab.com
diff --git a/spec/features/admin/admin_broadcast_messages_spec.rb b/spec/features/admin/admin_broadcast_messages_spec.rb
index bc957ec72e1..d6c63f66a9b 100644
--- a/spec/features/admin/admin_broadcast_messages_spec.rb
+++ b/spec/features/admin/admin_broadcast_messages_spec.rb
@@ -45,7 +45,7 @@ feature 'Admin Broadcast Messages', feature: true do
page.within('.broadcast-message-preview') do
expect(page).to have_selector('strong', text: 'Markdown')
- expect(page).to have_selector('img.emoji')
+ expect(page).to have_selector('gl-emoji[data-name="tada"]')
end
end
end
diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb
index 03daab12c8f..5099441dce2 100644
--- a/spec/features/admin/admin_settings_spec.rb
+++ b/spec/features/admin/admin_settings_spec.rb
@@ -34,6 +34,7 @@ feature 'Admin updates settings', feature: true do
fill_in 'Username', with: 'test_user'
fill_in 'service_push_channel', with: '#test_channel'
page.check('Notify only broken pipelines')
+ page.check('Notify only default branch')
check_all_events
click_on 'Save'
diff --git a/spec/features/boards/add_issues_modal_spec.rb b/spec/features/boards/add_issues_modal_spec.rb
index d17a418b8c3..1c0f97d8a1c 100644
--- a/spec/features/boards/add_issues_modal_spec.rb
+++ b/spec/features/boards/add_issues_modal_spec.rb
@@ -23,6 +23,20 @@ describe 'Issue Boards add issue modal', :feature, :js do
wait_for_vue_resource
end
+ it 'resets filtered search state' do
+ visit namespace_project_board_path(project.namespace, project, board, search: 'testing')
+
+ wait_for_vue_resource
+
+ click_button('Add issues')
+
+ page.within('.add-issues-modal') do
+ expect(find('.form-control').value).to eq('')
+ expect(page).to have_selector('.clear-search', visible: false)
+ expect(find('.form-control')[:placeholder]).to eq('Search or filter results...')
+ end
+ end
+
context 'modal interaction' do
it 'opens modal' do
click_button('Add issues')
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 0e305c52358..881f1fca4d1 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -11,12 +11,16 @@ describe 'Commits' do
stub_ci_pipeline_to_return_yaml_file
end
+ let(:creator) { create(:user) }
+
let!(:pipeline) do
create(:ci_pipeline,
project: project,
+ user: creator,
ref: project.default_branch,
sha: project.commit.sha,
- status: :success)
+ status: :success,
+ created_at: 5.months.ago)
end
context 'commit status is Generic Commit Status' do
@@ -80,7 +84,8 @@ describe 'Commits' do
it 'shows pipeline`s data' do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.user.name
+ expect(page).to have_content pipeline.created_at.strftime('%b %d, %Y')
end
end
@@ -150,7 +155,7 @@ describe 'Commits' do
it do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.user.name
expect(page).to have_link('Download artifacts')
expect(page).not_to have_link('Cancel running')
expect(page).not_to have_link('Retry')
@@ -169,7 +174,7 @@ describe 'Commits' do
it do
expect(page).to have_content pipeline.sha[0..7]
expect(page).to have_content pipeline.git_commit_message
- expect(page).to have_content pipeline.git_author_name
+ expect(page).to have_content pipeline.user.name
expect(page).not_to have_link('Download artifacts')
expect(page).not_to have_link('Cancel running')
expect(page).not_to have_link('Retry')
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index 62a2c54c94c..3642c0bfb5b 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -21,6 +21,11 @@ feature 'Dashboard shortcuts', feature: true, js: true do
find('body').native.send_key('m')
check_page_title('Merge Requests')
+
+ find('body').native.send_key('g')
+ find('body').native.send_key('t')
+
+ check_page_title('Todos')
end
def check_page_title(title)
diff --git a/spec/features/explore/groups_list_spec.rb b/spec/features/explore/groups_list_spec.rb
index 773ae4b38bc..9daaaa8e555 100644
--- a/spec/features/explore/groups_list_spec.rb
+++ b/spec/features/explore/groups_list_spec.rb
@@ -7,6 +7,7 @@ describe 'Explore Groups page', js: true, feature: true do
let!(:group) { create(:group) }
let!(:public_group) { create(:group, :public) }
let!(:private_group) { create(:group, :private) }
+ let!(:empty_project) { create(:empty_project, group: public_group) }
before do
group.add_owner(user)
@@ -43,4 +44,23 @@ describe 'Explore Groups page', js: true, feature: true do
expect(page).not_to have_content(private_group.full_name)
expect(page.all('.js-groups-list-holder .content-list li').length).to eq 2
end
+
+ it 'shows non-archived projects count' do
+ # Initially project is not archived
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
+
+ # Archive project
+ empty_project.archive!
+ visit explore_groups_path
+
+ # Check project count
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("0")
+
+ # Unarchive project
+ empty_project.unarchive!
+ visit explore_groups_path
+
+ # Check project count
+ expect(find('.js-groups-list-holder .content-list li:first-child .stats span:first-child')).to have_text("1")
+ end
end
diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb
index 0832a3656a8..f1ad4a55246 100644
--- a/spec/features/merge_requests/create_new_mr_spec.rb
+++ b/spec/features/merge_requests/create_new_mr_spec.rb
@@ -12,6 +12,33 @@ feature 'Create New Merge Request', feature: true, js: true do
login_as user
end
+ it 'selects the source branch sha when a tag with the same name exists' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ click_link 'New Merge Request'
+ expect(page).to have_content('Source branch')
+ expect(page).to have_content('Target branch')
+
+ first('.js-source-branch').click
+ first('.dropdown-source-branch .dropdown-content a', text: 'v1.1.0').click
+
+ expect(page).to have_content "b83d6e3"
+ end
+
+ it 'selects the target branch sha when a tag with the same name exists' do
+ visit namespace_project_merge_requests_path(project.namespace, project)
+
+ click_link 'New Merge Request'
+
+ expect(page).to have_content('Source branch')
+ expect(page).to have_content('Target branch')
+
+ first('.js-target-branch').click
+ first('.dropdown-target-branch .dropdown-content a', text: 'v1.1.0').click
+
+ expect(page).to have_content "b83d6e3"
+ end
+
it 'generates a diff for an orphaned branch' do
visit namespace_project_merge_requests_path(project.namespace, project)
@@ -46,6 +73,12 @@ feature 'Create New Merge Request', feature: true, js: true do
end
end
+ it 'populates source branch button' do
+ visit new_namespace_project_merge_request_path(project.namespace, project, change_branches: true, merge_request: { target_branch: 'master', source_branch: 'fix' })
+
+ expect(find('.js-source-branch')).to have_content('fix')
+ end
+
it 'allows to change the diff view' do
visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_branch: 'master', source_branch: 'fix' })
diff --git a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
index 0ceaf7bc830..79105b1ee46 100644
--- a/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
+++ b/spec/features/merge_requests/merge_immediately_with_pipeline_spec.rb
@@ -35,6 +35,8 @@ feature 'Merge immediately', :feature, :js do
click_link 'Merge Immediately'
expect(find('.js-merge-when-pipeline-succeeds-button')).to have_content('Merge in progress')
+
+ wait_for_ajax
end
end
end
diff --git a/spec/features/participants_autocomplete_spec.rb b/spec/features/participants_autocomplete_spec.rb
index c2545b0c259..decad589c23 100644
--- a/spec/features/participants_autocomplete_spec.rb
+++ b/spec/features/participants_autocomplete_spec.rb
@@ -1,101 +1,62 @@
require 'spec_helper'
-feature 'Member autocomplete', feature: true do
- include WaitForAjax
-
+feature 'Member autocomplete', :js do
let(:project) { create(:project, :public) }
let(:user) { create(:user) }
- let(:participant) { create(:user) }
let(:author) { create(:user) }
+ let(:note) { create(:note, noteable: noteable, project: noteable.project) }
before do
- allow_any_instance_of(Commit).to receive(:author).and_return(author)
- login_as user
+ note # actually create the note
+ login_as(user)
end
- shared_examples "open suggestions" do
- it 'displays suggestions' do
- expect(page).to have_selector('.atwho-view', visible: true)
- end
-
- it 'suggests author' do
- page.within('.atwho-view', visible: true) do
- expect(page).to have_content(author.username)
+ shared_examples "open suggestions when typing @" do
+ before do
+ page.within('.new-note') do
+ find('#note_note').send_keys('@')
end
end
- it 'suggests participant' do
+ it 'suggests noteable author and note author' do
page.within('.atwho-view', visible: true) do
- expect(page).to have_content(participant.username)
+ expect(page).to have_content(author.username)
+ expect(page).to have_content(note.author.username)
end
end
end
- context 'adding a new note on a Issue', js: true do
+ context 'adding a new note on a Issue' do
+ let(:noteable) { create(:issue, author: author, project: project) }
before do
- issue = create(:issue, author: author, project: project)
- create(:note, note: 'Ultralight Beam', noteable: issue,
- project: project, author: participant)
- visit_issue(project, issue)
+ visit namespace_project_issue_path(project.namespace, project, noteable)
end
- context 'when typing @' do
- include_examples "open suggestions"
- before do
- open_member_suggestions
- end
- end
+ include_examples "open suggestions when typing @"
end
- context 'adding a new note on a Merge Request ', js: true do
+ context 'adding a new note on a Merge Request' do
+ let(:noteable) do
+ create(:merge_request, source_project: project,
+ target_project: project, author: author)
+ end
before do
- merge = create(:merge_request, source_project: project, target_project: project, author: author)
- create(:note, note: 'Ultralight Beam', noteable: merge,
- project: project, author: participant)
- visit_merge_request(project, merge)
+ visit namespace_project_merge_request_path(project.namespace, project, noteable)
end
- context 'when typing @' do
- include_examples "open suggestions"
- before do
- open_member_suggestions
- end
- end
+ include_examples "open suggestions when typing @"
end
- context 'adding a new note on a Commit ', js: true do
- let(:commit) { project.commit }
+ context 'adding a new note on a Commit' do
+ let(:noteable) { project.commit }
+ let(:note) { create(:note_on_commit, project: project, commit_id: project.commit.id) }
before do
- allow(commit).to receive(:author).and_return(author)
- create(:note_on_commit, author: participant, project: project, commit_id: project.repository.commit.id, note: 'No More Parties in LA')
- visit_commit(project, commit)
- end
-
- context 'when typing @' do
- include_examples "open suggestions"
- before do
- open_member_suggestions
- end
- end
- end
+ allow_any_instance_of(Commit).to receive(:author).and_return(author)
- def open_member_suggestions
- page.within('.new-note') do
- find('#note_note').send_keys('@')
+ visit namespace_project_commit_path(project.namespace, project, noteable)
end
- wait_for_ajax
- end
-
- def visit_issue(project, issue)
- visit namespace_project_issue_path(project.namespace, project, issue)
- end
-
- def visit_merge_request(project, merge)
- visit namespace_project_merge_request_path(project.namespace, project, merge)
- end
- def visit_commit(project, commit)
- visit namespace_project_commit_path(project.namespace, project, commit)
+ include_examples "open suggestions when typing @"
end
end
diff --git a/spec/features/projects/blobs/user_create_spec.rb b/spec/features/projects/blobs/user_create_spec.rb
index 03d08c12612..5686868a0c4 100644
--- a/spec/features/projects/blobs/user_create_spec.rb
+++ b/spec/features/projects/blobs/user_create_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
feature 'New blob creation', feature: true, js: true do
include WaitForAjax
+ include TargetBranchHelpers
given(:user) { create(:user) }
given(:role) { :developer }
@@ -20,19 +21,6 @@ feature 'New blob creation', feature: true, js: true do
execute_script("ace.edit('editor').setValue('#{content}')")
end
- def select_branch_index(index)
- first('button.js-target-branch').click
- wait_for_ajax
- all('a[data-group="Branches"]')[index].click
- end
-
- def create_new_branch(name)
- first('button.js-target-branch').click
- click_link 'Create new branch'
- fill_in 'new_branch_name', with: name
- click_button 'Create'
- end
-
def commit_file
click_button 'Commit Changes'
end
@@ -53,12 +41,12 @@ feature 'New blob creation', feature: true, js: true do
context 'with different target branch' do
background do
edit_file
- select_branch_index(0)
+ select_branch('feature')
commit_file
end
scenario 'creates the blob in the different branch' do
- expect(page).to have_content 'test'
+ expect(page).to have_content 'feature'
expect(page).to have_content 'successfully created'
end
end
diff --git a/spec/features/projects/compare_spec.rb b/spec/features/projects/compare_spec.rb
index 030043d14aa..b2a3b111c9e 100644
--- a/spec/features/projects/compare_spec.rb
+++ b/spec/features/projects/compare_spec.rb
@@ -53,6 +53,7 @@ describe "Compare", js: true do
dropdown = find(".js-compare-#{dropdown_type}-dropdown")
dropdown.find(".compare-dropdown-toggle").click
dropdown.fill_in("Filter by Git revision", with: selection)
- find_link(selection, visible: true).click
+ wait_for_ajax
+ dropdown.find_all("a[data-ref=\"#{selection}\"]", visible: true).last.click
end
end
diff --git a/spec/features/projects/deploy_keys_spec.rb b/spec/features/projects/deploy_keys_spec.rb
new file mode 100644
index 00000000000..0b997f130ea
--- /dev/null
+++ b/spec/features/projects/deploy_keys_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe 'Project deploy keys', feature: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project_empty_repo) }
+
+ before do
+ project.team << [user, :master]
+ login_as(user)
+ end
+
+ describe 'removing key' do
+ before do
+ create(:deploy_keys_project, project: project)
+ end
+
+ it 'removes association between project and deploy key' do
+ visit namespace_project_settings_repository_path(project.namespace, project)
+
+ page.within '.deploy-keys' do
+ expect { click_on 'Remove' }
+ .to change { project.deploy_keys.count }.by(-1)
+ end
+ end
+ end
+end
diff --git a/spec/features/projects/group_links_spec.rb b/spec/features/projects/group_links_spec.rb
index 8b302a6aa23..4c28205da9b 100644
--- a/spec/features/projects/group_links_spec.rb
+++ b/spec/features/projects/group_links_spec.rb
@@ -8,7 +8,7 @@ feature 'Project group links', feature: true, js: true do
let!(:group) { create(:group) }
background do
- project.team << [master, :master]
+ project.add_master(master)
login_as(master)
end
@@ -29,4 +29,26 @@ feature 'Project group links', feature: true, js: true do
end
end
end
+
+ context 'nested group project' do
+ let!(:nested_group) { create(:group, parent: group) }
+ let!(:another_group) { create(:group) }
+ let!(:project) { create(:project, namespace: nested_group) }
+
+ background do
+ group.add_master(master)
+ another_group.add_master(master)
+ end
+
+ it 'does not show ancestors' do
+ visit namespace_project_settings_members_path(project.namespace, project)
+
+ click_link 'Search for a group'
+
+ page.within '.select2-drop' do
+ expect(page).to have_content(another_group.name)
+ expect(page).not_to have_content(group.name)
+ end
+ end
+ end
end
diff --git a/spec/features/projects/members/user_requests_access_spec.rb b/spec/features/projects/members/user_requests_access_spec.rb
index b64c15e0adc..de25d45f447 100644
--- a/spec/features/projects/members/user_requests_access_spec.rb
+++ b/spec/features/projects/members/user_requests_access_spec.rb
@@ -61,7 +61,7 @@ feature 'Projects > Members > User requests access', feature: true do
click_link('Settings')
end
- page.within('.page-with-layout-nav .sub-nav') do
+ page.within('.sub-nav') do
click_link('Members')
end
end
diff --git a/spec/features/projects/milestones/milestones_sorting_spec.rb b/spec/features/projects/milestones/milestones_sorting_spec.rb
new file mode 100644
index 00000000000..da3eaed707a
--- /dev/null
+++ b/spec/features/projects/milestones/milestones_sorting_spec.rb
@@ -0,0 +1,52 @@
+require 'spec_helper'
+
+feature 'Milestones sorting', :feature, :js do
+ include SortingHelper
+ let(:user) { create(:user) }
+ let(:project) { create(:empty_project, name: 'test', namespace: user.namespace) }
+
+ before do
+ # Milestones
+ create(:milestone,
+ due_date: 10.days.from_now,
+ created_at: 2.hours.ago,
+ title: "aaa", project: project)
+ create(:milestone,
+ due_date: 11.days.from_now,
+ created_at: 1.hour.ago,
+ title: "bbb", project: project)
+ login_as(user)
+ end
+
+ scenario 'visit project milestones and sort by due_date_asc' do
+ visit namespace_project_milestones_path(project.namespace, project)
+
+ expect(page).to have_button('Due soon')
+
+ # assert default sorting
+ within '.milestones' do
+ expect(page.all('ul.content-list > li').first.text).to include('aaa')
+ expect(page.all('ul.content-list > li').last.text).to include('bbb')
+ end
+
+ click_button 'Due soon'
+
+ sort_options = find('ul.dropdown-menu-sort li').all('a').collect(&:text)
+
+ expect(sort_options[0]).to eq('Due soon')
+ expect(sort_options[1]).to eq('Due later')
+ expect(sort_options[2]).to eq('Start soon')
+ expect(sort_options[3]).to eq('Start later')
+ expect(sort_options[4]).to eq('Name, ascending')
+ expect(sort_options[5]).to eq('Name, descending')
+
+ click_link 'Due later'
+
+ expect(page).to have_button('Due later')
+
+ within '.milestones' do
+ expect(page.all('ul.content-list > li').first.text).to include('bbb')
+ expect(page.all('ul.content-list > li').last.text).to include('aaa')
+ end
+ end
+end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 9f06e52ab55..5a53e48f5f8 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -45,7 +45,7 @@ describe 'Pipeline', :feature, :js do
include_context 'pipeline builds'
let(:project) { create(:project) }
- let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id) }
+ let(:pipeline) { create(:ci_pipeline, project: project, ref: 'master', sha: project.commit.id, user: user) }
before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index 162056671e0..2272b19bc8f 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -442,7 +442,7 @@ describe 'Pipelines', :feature, :js do
context 'when project is public' do
let(:project) { create(:project, :public) }
- it { expect(page).to have_content 'No pipelines to show' }
+ it { expect(page).to have_content 'Build with confidence' }
it { expect(page).to have_http_status(:success) }
end
diff --git a/spec/features/projects/user_create_dir_spec.rb b/spec/features/projects/user_create_dir_spec.rb
new file mode 100644
index 00000000000..2065abfb248
--- /dev/null
+++ b/spec/features/projects/user_create_dir_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+feature 'New directory creation', feature: true, js: true do
+ include WaitForAjax
+ include TargetBranchHelpers
+
+ given(:user) { create(:user) }
+ given(:role) { :developer }
+ given(:project) { create(:project) }
+
+ background do
+ login_as(user)
+ project.team << [user, role]
+ visit namespace_project_tree_path(project.namespace, project, 'master')
+ open_new_directory_modal
+ fill_in 'dir_name', with: 'new_directory'
+ end
+
+ def open_new_directory_modal
+ first('.add-to-tree').click
+ click_link 'New directory'
+ end
+
+ def create_directory
+ click_button 'Create directory'
+ end
+
+ context 'with default target branch' do
+ background do
+ create_directory
+ end
+
+ scenario 'creates the directory in the default branch' do
+ expect(page).to have_content 'master'
+ expect(page).to have_content 'The directory has been successfully created'
+ expect(page).to have_content 'new_directory'
+ end
+ end
+
+ context 'with different target branch' do
+ background do
+ select_branch('feature')
+ create_directory
+ end
+
+ scenario 'creates the directory in the different branch' do
+ expect(page).to have_content 'feature'
+ expect(page).to have_content 'The directory has been successfully created'
+ end
+ end
+
+ context 'with a new target branch' do
+ given(:new_branch_name) { 'new-feature' }
+
+ background do
+ create_new_branch(new_branch_name)
+ create_directory
+ end
+
+ scenario 'creates the directory in the new branch' do
+ expect(page).to have_content new_branch_name
+ expect(page).to have_content 'The directory has been successfully created'
+ end
+
+ scenario 'redirects to the merge request' do
+ expect(page).to have_content 'New Merge Request'
+ expect(page).to have_content "From #{new_branch_name} into master"
+ expect(page).to have_content 'Add new directory'
+ expect(current_path).to eq(new_namespace_project_merge_request_path(project.namespace, project))
+ end
+ end
+end
diff --git a/spec/features/user_callout_spec.rb b/spec/features/user_callout_spec.rb
index 336c4092c98..659cd7c7af7 100644
--- a/spec/features/user_callout_spec.rb
+++ b/spec/features/user_callout_spec.rb
@@ -2,6 +2,7 @@ require 'spec_helper'
describe 'User Callouts', js: true do
let(:user) { create(:user) }
+ let(:another_user) { create(:user) }
let(:project) { create(:empty_project, path: 'gitlab', name: 'sample') }
before do
@@ -32,6 +33,11 @@ describe 'User Callouts', js: true do
within('.user-callout') do
find('.close-user-callout').click
end
- expect(page).not_to have_selector('#user-callout')
+ expect(page).not_to have_selector('.user-callout')
+ end
+
+ it 'does not show callout on another users profile' do
+ visit user_path(another_user)
+ expect(page).not_to have_selector('.user-callout')
end
end
diff --git a/spec/features/users/projects_spec.rb b/spec/features/users/projects_spec.rb
new file mode 100644
index 00000000000..1d75fe434b0
--- /dev/null
+++ b/spec/features/users/projects_spec.rb
@@ -0,0 +1,31 @@
+require 'spec_helper'
+
+describe 'Projects tab on a user profile', :feature, :js do
+ include WaitForAjax
+
+ let(:user) { create(:user) }
+ let!(:project) { create(:empty_project, namespace: user.namespace) }
+ let!(:project2) { create(:empty_project, namespace: user.namespace) }
+
+ before do
+ allow(Project).to receive(:default_per_page).and_return(1)
+
+ login_as(user)
+
+ visit user_path(user)
+
+ page.within('.user-profile-nav') do
+ click_link('Personal projects')
+ end
+
+ wait_for_ajax
+ end
+
+ it 'paginates results' do
+ expect(page).to have_content(project2.name)
+
+ click_link('Next')
+
+ expect(page).to have_content(project.name)
+ end
+end
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
new file mode 100644
index 00000000000..581726c1d0e
--- /dev/null
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -0,0 +1,21 @@
+require 'rails_helper'
+
+describe AvatarsHelper do
+ let(:user) { create(:user) }
+
+ describe '#user_avatar' do
+ subject { helper.user_avatar(user: user) }
+
+ it "links to the user's profile" do
+ is_expected.to include("href=\"#{user_path(user)}\"")
+ end
+
+ it "has the user's name as title" do
+ is_expected.to include("title=\"#{user.name}\"")
+ end
+
+ it "contains the user's avatar image" do
+ is_expected.to include(CGI.escapeHTML(user.avatar_url(16)))
+ end
+ end
+end
diff --git a/spec/helpers/namespaces_helper_spec.rb b/spec/helpers/namespaces_helper_spec.rb
new file mode 100644
index 00000000000..e5143a0263d
--- /dev/null
+++ b/spec/helpers/namespaces_helper_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe NamespacesHelper, type: :helper do
+ let!(:admin) { create(:admin) }
+ let!(:admin_group) { create(:group, :private) }
+ let!(:user) { create(:user) }
+ let!(:user_group) { create(:group, :private) }
+
+ before do
+ admin_group.add_owner(admin)
+ user_group.add_owner(user)
+ end
+
+ describe '#namespaces_options' do
+ it 'returns groups without being a member for admin' do
+ allow(helper).to receive(:current_user).and_return(admin)
+
+ options = helper.namespaces_options(user_group.id, display_path: true, extra_group: user_group.id)
+
+ expect(options).to include(admin_group.name)
+ expect(options).to include(user_group.name)
+ end
+
+ it 'returns only allowed namespaces for user' do
+ allow(helper).to receive(:current_user).and_return(user)
+
+ options = helper.namespaces_options
+
+ expect(options).not_to include(admin_group.name)
+ expect(options).to include(user_group.name)
+ end
+ end
+end
diff --git a/spec/helpers/todos_helper_spec.rb b/spec/helpers/todos_helper_spec.rb
index 21e0e74e008..50060a0925d 100644
--- a/spec/helpers/todos_helper_spec.rb
+++ b/spec/helpers/todos_helper_spec.rb
@@ -1,40 +1,6 @@
require "spec_helper"
describe TodosHelper do
- include GitlabRoutingHelper
-
- describe '#todo_target_path' do
- let(:project) { create(:project) }
- let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
- let(:issue) { create(:issue, project: project) }
- let(:note) { create(:note_on_issue, noteable: issue, project: project) }
-
- let(:mr_todo) { build(:todo, project: project, target: merge_request) }
- let(:issue_todo) { build(:todo, project: project, target: issue) }
- let(:note_todo) { build(:todo, project: project, target: issue, note: note) }
- let(:build_failed_todo) { build(:todo, :build_failed, project: project, target: merge_request) }
-
- it 'returns correct path to the todo MR' do
- expect(todo_target_path(mr_todo)).
- to eq("/#{project.full_path}/merge_requests/#{merge_request.iid}")
- end
-
- it 'returns correct path to the todo issue' do
- expect(todo_target_path(issue_todo)).
- to eq("/#{project.full_path}/issues/#{issue.iid}")
- end
-
- it 'returns correct path to the todo note' do
- expect(todo_target_path(note_todo)).
- to eq("/#{project.full_path}/issues/#{issue.iid}#note_#{note.id}")
- end
-
- it 'returns correct path to build_todo MR when pipeline failed' do
- expect(todo_target_path(build_failed_todo)).
- to eq("/#{project.full_path}/merge_requests/#{merge_request.iid}/pipelines")
- end
- end
-
describe '#todo_projects_options' do
let(:projects) { create_list(:empty_project, 3) }
let(:user) { create(:user) }
diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb
new file mode 100644
index 00000000000..03f78de8e91
--- /dev/null
+++ b/spec/helpers/users_helper_spec.rb
@@ -0,0 +1,17 @@
+require 'rails_helper'
+
+describe UsersHelper do
+ let(:user) { create(:user) }
+
+ describe '#user_link' do
+ subject { helper.user_link(user) }
+
+ it "links to the user's profile" do
+ is_expected.to include("href=\"#{user_path(user)}\"")
+ end
+
+ it "has the user's email as title" do
+ is_expected.to include("title=\"#{user.email}\"")
+ end
+ end
+end
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index be31f644e20..73d18458366 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -1,10 +1,11 @@
-/* global Vue */
/* global List */
/* global ListLabel */
/* global listObj */
/* global boardsMockInterceptor */
/* global BoardService */
+import Vue from 'vue';
+
require('~/boards/models/list');
require('~/boards/models/label');
require('~/boards/stores/boards_store');
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 1d1069600fc..e21f4ca2bc0 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -1,12 +1,13 @@
/* eslint-disable comma-dangle, one-var, no-unused-vars */
-/* global Vue */
/* global BoardService */
/* global boardsMockInterceptor */
-/* global Cookies */
/* global listObj */
/* global listObjDuplicate */
/* global ListIssue */
+import Vue from 'vue';
+import Cookies from 'js-cookie';
+
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
require('~/boards/models/label');
diff --git a/spec/javascripts/boards/issue_card_spec.js b/spec/javascripts/boards/issue_card_spec.js
index 4340a571017..1a5e9e9fd07 100644
--- a/spec/javascripts/boards/issue_card_spec.js
+++ b/spec/javascripts/boards/issue_card_spec.js
@@ -1,9 +1,10 @@
-/* global Vue */
/* global ListUser */
/* global ListLabel */
/* global listObj */
/* global ListIssue */
+import Vue from 'vue';
+
require('~/boards/models/issue');
require('~/boards/models/label');
require('~/boards/models/list');
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index d49d3af33d9..66fc01fa1e5 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -1,5 +1,4 @@
/* eslint-disable comma-dangle */
-/* global Vue */
/* global boardsMockInterceptor */
/* global BoardService */
/* global List */
@@ -7,6 +6,8 @@
/* global listObj */
/* global listObjDuplicate */
+import Vue from 'vue';
+
require('~/lib/utils/url_utility');
require('~/boards/models/issue');
require('~/boards/models/label');
diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/javascripts/boards/modal_store_spec.js
index 1815847f3fa..80db816aff8 100644
--- a/spec/javascripts/boards/modal_store_spec.js
+++ b/spec/javascripts/boards/modal_store_spec.js
@@ -1,4 +1,3 @@
-/* global Vue */
/* global ListIssue */
require('~/boards/models/issue');
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 75efcc06585..bc2e092db65 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -33,7 +33,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
setTimeout(() => {
- expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
+ expect(component.$el.querySelector('.empty-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 1);
});
@@ -63,6 +64,7 @@ describe('Pipelines table in Commits and Merge requests', () => {
setTimeout(() => {
expect(component.$el.querySelectorAll('table > tbody > tr').length).toEqual(1);
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 0);
});
@@ -92,7 +94,8 @@ describe('Pipelines table in Commits and Merge requests', () => {
});
setTimeout(() => {
- expect(component.$el.querySelector('.js-blank-state-title').textContent).toContain('No pipelines to show');
+ expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
done();
}, 0);
});
diff --git a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
new file mode 100644
index 00000000000..50000c5a5f5
--- /dev/null
+++ b/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue';
+import limitWarningComp from '~/cycle_analytics/components/limit_warning_component';
+
+describe('Limit warning component', () => {
+ let component;
+ let LimitWarningComponent;
+
+ beforeEach(() => {
+ LimitWarningComponent = Vue.extend(limitWarningComp);
+ });
+
+ it('should not render if count is not exactly than 50', () => {
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 5,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('');
+
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 55,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('');
+ });
+
+ it('should render if count is exactly 50', () => {
+ component = new LimitWarningComponent({
+ propsData: {
+ count: 50,
+ },
+ }).$mount();
+
+ expect(component.$el.textContent.trim()).toBe('Showing 50 events');
+ });
+});
diff --git a/spec/javascripts/filtered_search/filtered_search_manager_spec.js b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
index 113161c21c6..848c7656a8d 100644
--- a/spec/javascripts/filtered_search/filtered_search_manager_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_manager_spec.js
@@ -58,7 +58,7 @@ const FilteredSearchSpecHelper = require('../helpers/filtered_search_spec_helper
});
describe('search', () => {
- const defaultParams = '?scope=all&utf8=✓&state=opened';
+ const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
it('should search with a single word', (done) => {
input.value = 'searchTerm';
diff --git a/spec/javascripts/fixtures/pipelines.html.haml b/spec/javascripts/fixtures/pipelines.html.haml
new file mode 100644
index 00000000000..418a38a0e2e
--- /dev/null
+++ b/spec/javascripts/fixtures/pipelines.html.haml
@@ -0,0 +1,14 @@
+%div
+ #pipelines-list-vue{ data: { endpoint: 'foo',
+ "css-class" => 'foo',
+ "help-page-path" => 'foo',
+ "new-pipeline-path" => 'foo',
+ "can-create-pipeline" => 'true',
+ "all-path" => 'foo',
+ "pending-path" => 'foo',
+ "running-path" => 'foo',
+ "finished-path" => 'foo',
+ "branches-path" => 'foo',
+ "tags-path" => 'foo',
+ "has-ci" => 'foo',
+ "ci-lint-path" => 'foo' } }
diff --git a/spec/javascripts/fixtures/pipelines_table.html.haml b/spec/javascripts/fixtures/pipelines_table.html.haml
index fbe4a434f76..ad1682704bb 100644
--- a/spec/javascripts/fixtures/pipelines_table.html.haml
+++ b/spec/javascripts/fixtures/pipelines_table.html.haml
@@ -1,2 +1 @@
-#commit-pipeline-table-view{ data: { endpoint: "endpoint" } }
-.pipeline-svgs{ data: { "commit_icon_svg": "svg"} }
+#commit-pipeline-table-view{ data: { endpoint: "endpoint", "help-page-path": "foo" } }
diff --git a/spec/javascripts/issuable_time_tracker_spec.js b/spec/javascripts/issuable_time_tracker_spec.js
index cb068a4f879..0a830f25e29 100644
--- a/spec/javascripts/issuable_time_tracker_spec.js
+++ b/spec/javascripts/issuable_time_tracker_spec.js
@@ -1,7 +1,7 @@
-/* eslint-disable */
+/* eslint-disable no-unused-vars, space-before-function-paren, func-call-spacing, no-spaced-func, semi, max-len, quotes, space-infix-ops, padded-blocks */
+
+import Vue from 'vue';
-require('jquery');
-require('vue');
require('~/issuable/time_tracking/components/time_tracker');
function initTimeTrackingComponent(opts) {
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index f4d3e77e515..d2e24eb7eb2 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -163,5 +163,72 @@ require('~/lib/utils/common_utils');
expect(gl.utils.isMetaClick(e)).toBe(true);
});
});
+
+ describe('gl.utils.backOff', () => {
+ it('solves the promise from the callback', (done) => {
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff((next, stop) => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then((resp) => {
+ stop(resp);
+ })
+ )).then((respBackoff) => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ done();
+ });
+ });
+
+ it('catches the rejected promise from the callback ', (done) => {
+ const errorMessage = 'Mistakes were made!';
+ gl.utils.backOff((next, stop) => {
+ new Promise((resolve, reject) => {
+ reject(new Error(errorMessage));
+ }).then((resp) => {
+ stop(resp);
+ }).catch(err => stop(err));
+ }).catch((errBackoffResp) => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe(errorMessage);
+ done();
+ });
+ });
+
+ it('solves the promise correctly after retrying a third time', (done) => {
+ let numberOfCalls = 1;
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff((next, stop) => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then((resp) => {
+ if (numberOfCalls < 3) {
+ numberOfCalls += 1;
+ next();
+ } else {
+ stop(resp);
+ }
+ })
+ )).then((respBackoff) => {
+ expect(respBackoff).toBe(expectedResponseValue);
+ expect(numberOfCalls).toBe(3);
+ done();
+ });
+ }, 10000);
+
+ it('rejects the backOff promise after timing out', (done) => {
+ const expectedResponseValue = 'Success!';
+ gl.utils.backOff(next => (
+ new Promise((resolve) => {
+ resolve(expectedResponseValue);
+ }).then(() => {
+ setTimeout(next(), 5000); // it will time out
+ })
+ ), 3000).catch((errBackoffResp) => {
+ expect(errBackoffResp instanceof Error).toBe(true);
+ expect(errBackoffResp.message).toBe('BACKOFF_TIMEOUT');
+ done();
+ });
+ }, 10000);
+ });
});
})();
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
new file mode 100644
index 00000000000..c794a632417
--- /dev/null
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -0,0 +1,163 @@
+import Vue from 'vue';
+import VueResource from 'vue-resource';
+import Poll from '~/lib/utils/poll';
+
+Vue.use(VueResource);
+
+class ServiceMock {
+ constructor(endpoint) {
+ this.service = Vue.resource(endpoint);
+ }
+
+ fetch() {
+ return this.service.get();
+ }
+}
+
+describe('Poll', () => {
+ let callbacks;
+
+ beforeEach(() => {
+ callbacks = {
+ success: () => {},
+ error: () => {},
+ };
+
+ spyOn(callbacks, 'success');
+ spyOn(callbacks, 'error');
+ });
+
+ it('calls the success callback when no header for interval is provided', (done) => {
+ const successInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200 }));
+ };
+
+ Vue.http.interceptors.push(successInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, successInterceptor);
+ });
+
+ it('calls the error callback whe the http request returns an error', (done) => {
+ const errorInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 500 }));
+ };
+
+ Vue.http.interceptors.push(errorInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).not.toHaveBeenCalled();
+ expect(callbacks.error).toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, errorInterceptor);
+ });
+
+ it('should call the success callback when the interval header is -1', (done) => {
+ const intervalInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': -1 } }));
+ };
+
+ Vue.http.interceptors.push(intervalInterceptor);
+
+ new Poll({
+ resource: new ServiceMock('endpoint'),
+ method: 'fetch',
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 0);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, intervalInterceptor);
+ });
+
+ it('starts polling when http status is 200 and interval header is provided', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: callbacks.success,
+ errorCallback: callbacks.error,
+ }).makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(2);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(callbacks.success).toHaveBeenCalled();
+ expect(callbacks.error).not.toHaveBeenCalled();
+ done();
+ }, 5);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+
+ describe('stop', () => {
+ it('stops polling when method is called', (done) => {
+ const pollInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), { status: 200, headers: { 'poll-interval': 2 } }));
+ };
+
+ Vue.http.interceptors.push(pollInterceptor);
+
+ const service = new ServiceMock('endpoint');
+ spyOn(service, 'fetch').and.callThrough();
+
+ const Polling = new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: () => {
+ Polling.stop();
+ },
+ errorCallback: callbacks.error,
+ });
+
+ spyOn(Polling, 'stop').and.callThrough();
+
+ Polling.makeRequest();
+
+ setTimeout(() => {
+ expect(service.fetch.calls.count()).toEqual(1);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 1 });
+ expect(Polling.stop).toHaveBeenCalled();
+ done();
+ }, 100);
+
+ Vue.http.interceptors = _.without(Vue.http.interceptors, pollInterceptor);
+ });
+ });
+});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index 5cdb6473eda..d658f680f97 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -8,9 +8,6 @@ jasmine.getJSONFixtures().fixturesPath = 'base/spec/javascripts/fixtures';
require('~/commons/index.js');
window.$ = window.jQuery = require('jquery');
window._ = require('underscore');
-window.Cookies = require('js-cookie');
-window.Vue = require('vue');
-window.Vue.use(require('vue-resource'));
// stub expected globals
window.gl = window.gl || {};
@@ -38,7 +35,8 @@ testsContext.keys().forEach(function (path) {
if (process.env.BABEL_ENV === 'coverage') {
// exempt these files from the coverage report
const troubleMakers = [
- './blob_edit/blob_edit_bundle.js',
+ './blob_edit/blob_bundle.js',
+ './boards/boards_bundle.js',
'./cycle_analytics/components/stage_plan_component.js',
'./cycle_analytics/components/stage_staging_component.js',
'./cycle_analytics/components/stage_test_component.js',
diff --git a/spec/javascripts/user_callout_spec.js b/spec/javascripts/user_callout_spec.js
index 205e72af600..2398149d3ad 100644
--- a/spec/javascripts/user_callout_spec.js
+++ b/spec/javascripts/user_callout_spec.js
@@ -1,7 +1,7 @@
-const UserCallout = require('~/user_callout');
+import Cookies from 'js-cookie';
+import UserCallout from '~/user_callout';
const USER_CALLOUT_COOKIE = 'user_callout_dismissed';
-const Cookie = window.Cookies;
describe('UserCallout', function () {
const fixtureName = 'static/user_callout.html.raw';
@@ -9,7 +9,7 @@ describe('UserCallout', function () {
beforeEach(() => {
loadFixtures(fixtureName);
- Cookie.remove(USER_CALLOUT_COOKIE);
+ Cookies.remove(USER_CALLOUT_COOKIE);
this.userCallout = new UserCallout();
this.closeButton = $('.close-user-callout');
@@ -18,25 +18,25 @@ describe('UserCallout', function () {
});
it('does not show when cookie is set not defined', () => {
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeUndefined();
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeUndefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
it('shows when cookie is set to false', () => {
- Cookie.set(USER_CALLOUT_COOKIE, 'false');
+ Cookies.set(USER_CALLOUT_COOKIE, 'false');
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBeDefined();
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBeDefined();
expect(this.userCalloutContainer.is(':visible')).toBe(true);
});
it('hides when user clicks on the dismiss-icon', () => {
this.closeButton.click();
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true');
});
it('hides when user clicks on the "check it out" button', () => {
this.userCalloutBtn.click();
- expect(Cookie.get(USER_CALLOUT_COOKIE)).toBe('true');
+ expect(Cookies.get(USER_CALLOUT_COOKIE)).toBe('true');
});
});
@@ -46,7 +46,7 @@ describe('UserCallout when cookie is present', function () {
beforeEach(() => {
loadFixtures(fixtureName);
- Cookie.set(USER_CALLOUT_COOKIE, 'true');
+ Cookies.set(USER_CALLOUT_COOKIE, 'true');
this.userCallout = new UserCallout();
this.userCalloutContainer = $('.user-callout');
});
diff --git a/spec/javascripts/vue_pipelines_index/empty_state_spec.js b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
new file mode 100644
index 00000000000..733337168dc
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/empty_state_spec.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import emptyStateComp from '~/vue_pipelines_index/components/empty_state';
+
+describe('Pipelines Empty State', () => {
+ let component;
+ let EmptyStateComponent;
+
+ beforeEach(() => {
+ EmptyStateComponent = Vue.extend(emptyStateComp);
+
+ component = new EmptyStateComponent({
+ propsData: {
+ helpPagePath: 'foo',
+ },
+ }).$mount();
+ });
+
+ it('should render empty state SVG', () => {
+ expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
+ });
+
+ it('should render emtpy state information', () => {
+ expect(component.$el.querySelector('h4').textContent).toContain('Build with confidence');
+
+ expect(
+ component.$el.querySelector('p').textContent,
+ ).toContain('Continous Integration can help catch bugs by running your tests automatically');
+
+ expect(
+ component.$el.querySelector('p').textContent,
+ ).toContain('Continuous Deployment can help you deliver code to your product environment');
+ });
+
+ it('should render a link with provided help path', () => {
+ expect(component.$el.querySelector('.btn-info').getAttribute('href')).toEqual('foo');
+ expect(component.$el.querySelector('.btn-info').textContent).toContain('Get started with Pipelines');
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/error_state_spec.js b/spec/javascripts/vue_pipelines_index/error_state_spec.js
new file mode 100644
index 00000000000..524e018b1fa
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/error_state_spec.js
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+import errorStateComp from '~/vue_pipelines_index/components/error_state';
+
+describe('Pipelines Error State', () => {
+ let component;
+ let ErrorStateComponent;
+
+ beforeEach(() => {
+ ErrorStateComponent = Vue.extend(errorStateComp);
+
+ component = new ErrorStateComponent().$mount();
+ });
+
+ it('should render error state SVG', () => {
+ expect(component.$el.querySelector('.svg-content svg')).toBeDefined();
+ });
+
+ it('should render emtpy state information', () => {
+ expect(
+ component.$el.querySelector('h4').textContent,
+ ).toContain('The API failed to fetch the pipelines');
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/mock_data.js b/spec/javascripts/vue_pipelines_index/mock_data.js
new file mode 100644
index 00000000000..2365a662b9f
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/mock_data.js
@@ -0,0 +1,107 @@
+export default {
+ pipelines: [{
+ id: 115,
+ user: {
+ name: 'Root',
+ username: 'root',
+ id: 1,
+ state: 'active',
+ avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3000/root',
+ },
+ path: '/root/review-app/pipelines/115',
+ details: {
+ status: {
+ icon: 'icon_status_failed',
+ text: 'failed',
+ label: 'failed',
+ group: 'failed',
+ has_details: true,
+ details_path: '/root/review-app/pipelines/115',
+ },
+ duration: null,
+ finished_at: '2017-03-17T19:00:15.996Z',
+ stages: [{
+ name: 'build',
+ title: 'build: failed',
+ status: {
+ icon: 'icon_status_failed',
+ text: 'failed',
+ label: 'failed',
+ group: 'failed',
+ has_details: true,
+ details_path: '/root/review-app/pipelines/115#build',
+ },
+ path: '/root/review-app/pipelines/115#build',
+ dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=build',
+ },
+ {
+ name: 'review',
+ title: 'review: skipped',
+ status: {
+ icon: 'icon_status_skipped',
+ text: 'skipped',
+ label: 'skipped',
+ group: 'skipped',
+ has_details: true,
+ details_path: '/root/review-app/pipelines/115#review',
+ },
+ path: '/root/review-app/pipelines/115#review',
+ dropdown_path: '/root/review-app/pipelines/115/stage.json?stage=review',
+ }],
+ artifacts: [],
+ manual_actions: [{
+ name: 'stop_review',
+ path: '/root/review-app/builds/3766/play',
+ }],
+ },
+ flags: {
+ latest: true,
+ triggered: false,
+ stuck: false,
+ yaml_errors: false,
+ retryable: true,
+ cancelable: false,
+ },
+ ref: {
+ name: 'thisisabranch',
+ path: '/root/review-app/tree/thisisabranch',
+ tag: false,
+ branch: true,
+ },
+ commit: {
+ id: '9e87f87625b26c42c59a2ee0398f81d20cdfe600',
+ short_id: '9e87f876',
+ title: 'Update README.md',
+ created_at: '2017-03-15T22:58:28.000+00:00',
+ parent_ids: ['3744f9226e699faec2662a8b267e5d3fd0bfff0e'],
+ message: 'Update README.md',
+ author_name: 'Root',
+ author_email: 'admin@example.com',
+ authored_date: '2017-03-15T22:58:28.000+00:00',
+ committer_name: 'Root',
+ committer_email: 'admin@example.com',
+ committed_date: '2017-03-15T22:58:28.000+00:00',
+ author: {
+ name: 'Root',
+ username: 'root',
+ id: 1,
+ state: 'active',
+ avatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ web_url: 'http://localhost:3000/root',
+ },
+ author_gravatar_url: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80\u0026d=identicon',
+ commit_url: 'http://localhost:3000/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
+ commit_path: '/root/review-app/commit/9e87f87625b26c42c59a2ee0398f81d20cdfe600',
+ },
+ retry_path: '/root/review-app/pipelines/115/retry',
+ created_at: '2017-03-15T22:58:33.436Z',
+ updated_at: '2017-03-17T19:00:15.997Z',
+ }],
+ count: {
+ all: 52,
+ running: 0,
+ pending: 0,
+ finished: 52,
+ },
+};
diff --git a/spec/javascripts/vue_pipelines_index/nav_controls_spec.js b/spec/javascripts/vue_pipelines_index/nav_controls_spec.js
new file mode 100644
index 00000000000..659c4854a56
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/nav_controls_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import navControlsComp from '~/vue_pipelines_index/components/nav_controls';
+
+describe('Pipelines Nav Controls', () => {
+ let NavControlsComponent;
+
+ beforeEach(() => {
+ NavControlsComponent = Vue.extend(navControlsComp);
+ });
+
+ it('should render link to create a new pipeline', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-create').textContent).toContain('Run Pipeline');
+ expect(component.$el.querySelector('.btn-create').getAttribute('href')).toEqual(mockData.newPipelinePath);
+ });
+
+ it('should not render link to create pipeline if no permission is provided', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: false,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-create')).toEqual(null);
+ });
+
+ it('should render link for CI lint', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-default').textContent).toContain('CI Lint');
+ expect(component.$el.querySelector('.btn-default').getAttribute('href')).toEqual(mockData.ciLintPath);
+ });
+
+ it('should render link to help page when CI is not enabled', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: false,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-info').textContent).toContain('Get started with Pipelines');
+ expect(component.$el.querySelector('.btn-info').getAttribute('href')).toEqual(mockData.helpPagePath);
+ });
+
+ it('should not render link to help page when CI is enabled', () => {
+ const mockData = {
+ newPipelinePath: 'foo',
+ hasCiEnabled: true,
+ helpPagePath: 'foo',
+ ciLintPath: 'foo',
+ canCreatePipeline: true,
+ };
+
+ const component = new NavControlsComponent({
+ propsData: mockData,
+ }).$mount();
+
+ expect(component.$el.querySelector('.btn-info')).toEqual(null);
+ });
+});
diff --git a/spec/javascripts/vue_pipelines_index/pipelines_spec.js b/spec/javascripts/vue_pipelines_index/pipelines_spec.js
new file mode 100644
index 00000000000..725f6cb2d7a
--- /dev/null
+++ b/spec/javascripts/vue_pipelines_index/pipelines_spec.js
@@ -0,0 +1,114 @@
+import Vue from 'vue';
+import pipelinesComp from '~/vue_pipelines_index/pipelines';
+import Store from '~/vue_pipelines_index/stores/pipelines_store';
+import pipelinesData from './mock_data';
+
+describe('Pipelines', () => {
+ preloadFixtures('static/pipelines.html.raw');
+
+ let PipelinesComponent;
+
+ beforeEach(() => {
+ loadFixtures('static/pipelines.html.raw');
+
+ PipelinesComponent = Vue.extend(pipelinesComp);
+ });
+
+ describe('successfull request', () => {
+ describe('with pipelines', () => {
+ const pipelinesInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify(pipelinesData), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(pipelinesInterceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, pipelinesInterceptor,
+ );
+ });
+
+ it('should render table', (done) => {
+ const component = new PipelinesComponent({
+ propsData: {
+ store: new Store(),
+ },
+ }).$mount();
+
+ setTimeout(() => {
+ expect(component.$el.querySelector('.table-holder')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+
+ describe('without pipelines', () => {
+ const emptyInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 200,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(emptyInterceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, emptyInterceptor,
+ );
+ });
+
+ it('should render empty state', (done) => {
+ const component = new PipelinesComponent({
+ propsData: {
+ store: new Store(),
+ },
+ }).$mount();
+
+ setTimeout(() => {
+ expect(component.$el.querySelector('.empty-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+ });
+
+ describe('unsuccessfull request', () => {
+ const errorInterceptor = (request, next) => {
+ next(request.respondWith(JSON.stringify([]), {
+ status: 500,
+ }));
+ };
+
+ beforeEach(() => {
+ Vue.http.interceptors.push(errorInterceptor);
+ });
+
+ afterEach(() => {
+ Vue.http.interceptors = _.without(
+ Vue.http.interceptors, errorInterceptor,
+ );
+ });
+
+ it('should render error state', (done) => {
+ const component = new PipelinesComponent({
+ propsData: {
+ store: new Store(),
+ },
+ }).$mount();
+
+ setTimeout(() => {
+ expect(component.$el.querySelector('.js-pipelines-error-state')).toBeDefined();
+ expect(component.$el.querySelector('.realtime-loading')).toBe(null);
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/lib/gitlab/ci/build/step_spec.rb b/spec/lib/gitlab/ci/build/step_spec.rb
index 2a314a744ca..49457b129e3 100644
--- a/spec/lib/gitlab/ci/build/step_spec.rb
+++ b/spec/lib/gitlab/ci/build/step_spec.rb
@@ -25,7 +25,7 @@ describe Gitlab::Ci::Build::Step do
end
context 'when after_script is not empty' do
- let(:job) { create(:ci_build, options: { after_script: "ls -la\ndate" }) }
+ let(:job) { create(:ci_build, options: { after_script: ['ls -la', 'date'] }) }
it 'fabricates an object' do
expect(subject.name).to eq(:after_script)
diff --git a/spec/lib/gitlab/git/blob_snippet_spec.rb b/spec/lib/gitlab/git/blob_snippet_spec.rb
index 17d6be470ac..d6d365f6492 100644
--- a/spec/lib/gitlab/git/blob_snippet_spec.rb
+++ b/spec/lib/gitlab/git/blob_snippet_spec.rb
@@ -3,7 +3,7 @@
require "spec_helper"
describe Gitlab::Git::BlobSnippet, seed_helper: true do
- describe :data do
+ describe '#data' do
context 'empty lines' do
let(:snippet) { Gitlab::Git::BlobSnippet.new('master', nil, nil, nil) }
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 8049e2c120d..b883526151e 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -5,7 +5,7 @@ require "spec_helper"
describe Gitlab::Git::Blob, seed_helper: true do
let(:repository) { Gitlab::Git::Repository.new(TEST_REPO_PATH) }
- describe :initialize do
+ describe 'initialize' do
let(:blob) { Gitlab::Git::Blob.new(name: 'test') }
it 'handles nil data' do
@@ -15,7 +15,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe :find do
+ describe '.find' do
context 'file in subdir' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "files/ruby/popen.rb") }
@@ -101,7 +101,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe :raw do
+ describe '.raw' do
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
it { expect(raw_blob.id).to eq(SeedRepo::RubyBlob::ID) }
it { expect(raw_blob.data[0..10]).to eq("require \'fi") }
@@ -222,7 +222,7 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
- describe :lfs_pointers do
+ describe 'lfs_pointers' do
context 'file a valid lfs pointer' do
let(:blob) do
Gitlab::Git::Blob.find(
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index e1be6784c20..5cf4631fbfc 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -65,7 +65,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
context 'Class methods' do
- describe :find do
+ describe '.find' do
it "should return first head commit if without params" do
expect(Gitlab::Git::Commit.last(repository).id).to eq(
repository.raw.head.target.oid
@@ -103,7 +103,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :last_for_path do
+ describe '.last_for_path' do
context 'no path' do
subject { Gitlab::Git::Commit.last_for_path(repository, 'master') }
@@ -132,7 +132,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe "where" do
+ describe '.where' do
context 'path is empty string' do
subject do
commits = Gitlab::Git::Commit.where(
@@ -230,7 +230,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :between do
+ describe '.between' do
subject do
commits = Gitlab::Git::Commit.between(repository, SeedRepo::Commit::PARENT_ID, SeedRepo::Commit::ID)
commits.map { |c| c.id }
@@ -243,7 +243,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
it { is_expected.not_to include(SeedRepo::FirstCommit::ID) }
end
- describe :find_all do
+ describe '.find_all' do
context 'max_count' do
subject do
commits = Gitlab::Git::Commit.find_all(
@@ -304,7 +304,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :init_from_rugged do
+ describe '#init_from_rugged' do
let(:gitlab_commit) { Gitlab::Git::Commit.new(rugged_commit) }
subject { gitlab_commit }
@@ -314,7 +314,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :init_from_hash do
+ describe '#init_from_hash' do
let(:commit) { Gitlab::Git::Commit.new(sample_commit_hash) }
subject { commit }
@@ -329,7 +329,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :stats do
+ describe '#stats' do
subject { commit.stats }
describe '#additions' do
@@ -343,25 +343,25 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :to_diff do
+ describe '#to_diff' do
subject { commit.to_diff }
it { is_expected.not_to include "From #{SeedRepo::Commit::ID}" }
it { is_expected.to include 'diff --git a/files/ruby/popen.rb b/files/ruby/popen.rb'}
end
- describe :has_zero_stats? do
+ describe '#has_zero_stats?' do
it { expect(commit.has_zero_stats?).to eq(false) }
end
- describe :to_patch do
+ describe '#to_patch' do
subject { commit.to_patch }
it { is_expected.to include "From #{SeedRepo::Commit::ID}" }
it { is_expected.to include 'diff --git a/files/ruby/popen.rb b/files/ruby/popen.rb'}
end
- describe :to_hash do
+ describe '#to_hash' do
let(:hash) { commit.to_hash }
subject { hash }
@@ -373,7 +373,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
end
end
- describe :diffs do
+ describe '#diffs' do
subject { commit.diffs }
it { is_expected.to be_kind_of Gitlab::Git::DiffCollection }
@@ -381,7 +381,7 @@ describe Gitlab::Git::Commit, seed_helper: true do
it { expect(subject.first).to be_kind_of Gitlab::Git::Diff }
end
- describe :ref_names do
+ describe '#ref_names' do
let(:commit) { Gitlab::Git::Commit.find(repository, 'master') }
subject { commit.ref_names(repository) }
diff --git a/spec/lib/gitlab/git/compare_spec.rb b/spec/lib/gitlab/git/compare_spec.rb
index f66b68e4218..e28debe1494 100644
--- a/spec/lib/gitlab/git/compare_spec.rb
+++ b/spec/lib/gitlab/git/compare_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
let(:compare) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, false) }
let(:compare_straight) { Gitlab::Git::Compare.new(repository, SeedRepo::BigCommit::ID, SeedRepo::Commit::ID, true) }
- describe :commits do
+ describe '#commits' do
subject do
compare.commits.map(&:id)
end
@@ -42,7 +42,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
end
end
- describe :diffs do
+ describe '#diffs' do
subject do
compare.diffs.map(&:new_path)
end
@@ -67,7 +67,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
end
end
- describe :same do
+ describe '#same' do
subject do
compare.same
end
@@ -81,7 +81,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
end
end
- describe :commits_straight do
+ describe '#commits', 'straight compare' do
subject do
compare_straight.commits.map(&:id)
end
@@ -94,7 +94,7 @@ describe Gitlab::Git::Compare, seed_helper: true do
it { is_expected.not_to include(SeedRepo::BigCommit::PARENT_ID) }
end
- describe :diffs_straight do
+ describe '#diffs', 'straight compare' do
subject do
compare_straight.diffs.map(&:new_path)
end
diff --git a/spec/lib/gitlab/git/diff_collection_spec.rb b/spec/lib/gitlab/git/diff_collection_spec.rb
index 47bdd7310d5..122c93dcd69 100644
--- a/spec/lib/gitlab/git/diff_collection_spec.rb
+++ b/spec/lib/gitlab/git/diff_collection_spec.rb
@@ -24,7 +24,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
it { is_expected.to be_kind_of ::Array }
end
- describe :decorate! do
+ describe '#decorate!' do
let(:file_count) { 3 }
it 'modifies the array in place' do
@@ -302,7 +302,7 @@ describe Gitlab::Git::DiffCollection, seed_helper: true do
end
end
- describe :each do
+ describe '#each' do
context 'when diff are too large' do
let(:collection) do
Gitlab::Git::DiffCollection.new([{ diff: 'a' * 204800 }])
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 688e2a75373..83d2ff8f9b3 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -11,7 +11,7 @@ describe Gitlab::Git::Tree, seed_helper: true do
it { expect(tree.select(&:file?).size).to eq(10) }
it { expect(tree.select(&:submodule?).size).to eq(2) }
- describe :dir do
+ describe '#dir?' do
let(:dir) { tree.select(&:dir?).first }
it { expect(dir).to be_kind_of Gitlab::Git::Tree }
@@ -41,7 +41,7 @@ describe Gitlab::Git::Tree, seed_helper: true do
end
end
- describe :file do
+ describe '#file?' do
let(:file) { tree.select(&:file?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
@@ -50,21 +50,21 @@ describe Gitlab::Git::Tree, seed_helper: true do
it { expect(file.name).to eq('.gitignore') }
end
- describe :readme do
+ describe '#readme?' do
let(:file) { tree.select(&:readme?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.name).to eq('README.md') }
end
- describe :contributing do
+ describe '#contributing?' do
let(:file) { tree.select(&:contributing?).first }
it { expect(file).to be_kind_of Gitlab::Git::Tree }
it { expect(file.name).to eq('CONTRIBUTING.md') }
end
- describe :submodule do
+ describe '#submodule?' do
let(:submodule) { tree.select(&:submodule?).first }
it { expect(submodule).to be_kind_of Gitlab::Git::Tree }
diff --git a/spec/lib/gitlab/git/util_spec.rb b/spec/lib/gitlab/git/util_spec.rb
index 8d43b570e98..bcca4d4c746 100644
--- a/spec/lib/gitlab/git/util_spec.rb
+++ b/spec/lib/gitlab/git/util_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Gitlab::Git::Util do
- describe :count_lines do
+ describe '#count_lines' do
[
["", 0],
["foo", 1],
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 2f3bd4393b7..346cf0d117c 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -57,7 +57,7 @@ describe Gitlab::LDAP::User, lib: true do
end
end
- describe :find_or_create do
+ describe 'find or create' do
it "finds the user if already existing" do
create(:omniauth_user, extern_uid: 'my-uid', provider: 'ldapmain')
diff --git a/spec/mailers/emails/profile_spec.rb b/spec/mailers/emails/profile_spec.rb
index e1877d5fde0..5ca936f28f0 100644
--- a/spec/mailers/emails/profile_spec.rb
+++ b/spec/mailers/emails/profile_spec.rb
@@ -5,6 +5,16 @@ describe Notify do
include EmailSpec::Matchers
include_context 'gitlab email notification'
+ shared_examples 'a new user email' do
+ it 'is sent to the new user with the correct subject and body' do
+ aggregate_failures do
+ is_expected.to deliver_to new_user_address
+ is_expected.to have_subject(/^Account was created for you$/i)
+ is_expected.to have_body_text(new_user_address)
+ end
+ end
+ end
+
describe 'profile notifications' do
describe 'for new users, the email' do
let(:example_site_path) { root_path }
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 6ee91576676..4b72eb2eaa3 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -24,14 +24,14 @@ describe Notify do
let(:previous_assignee) { create(:user, name: 'Previous Assignee') }
shared_examples 'an assignee email' do
- it 'is sent as the author' do
- sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(current_user.name)
- expect(sender.address).to eq(gitlab_sender)
- end
+ it 'is sent to the assignee as the author' do
+ sender = subject.header[:from].addrs.first
- it 'is sent to the assignee' do
- is_expected.to deliver_to assignee.email
+ aggregate_failures do
+ expect(sender.display_name).to eq(current_user.name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(subject).to deliver_to(assignee.email)
+ end
end
end
@@ -49,12 +49,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue)
- end
-
- it 'contains a link to the new issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue)
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
context 'when enabled email_author_in_body' do
@@ -63,7 +62,7 @@ describe Notify do
end
it 'contains a link to note author' do
- is_expected.to have_html_escaped_body_text issue.author_name
+ is_expected.to have_html_escaped_body_text(issue.author_name)
is_expected.to have_body_text 'wrote:'
end
end
@@ -95,20 +94,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains the name of the previous assignee' do
- is_expected.to have_html_escaped_body_text previous_assignee.name
- end
-
- it 'contains the name of the new assignee' do
- is_expected.to have_html_escaped_body_text assignee.name
- end
-
- it 'contains a link to the issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_html_escaped_body_text(previous_assignee.name)
+ is_expected.to have_html_escaped_body_text(assignee.name)
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
end
@@ -129,16 +121,12 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains the names of the added labels' do
- is_expected.to have_body_text 'foo, bar, and baz'
- end
-
- it 'contains a link to the issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text('foo, bar, and baz')
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
end
@@ -158,20 +146,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains the new status' do
- is_expected.to have_body_text status
- end
-
- it 'contains the user name' do
- is_expected.to have_html_escaped_body_text current_user.name
- end
-
- it 'contains a link to the issue' do
- is_expected.to have_body_text(namespace_project_issue_path project.namespace, project, issue)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(status)
+ is_expected.to have_html_escaped_body_text(current_user.name)
+ is_expected.to have_body_text(namespace_project_issue_path project.namespace, project, issue)
+ end
end
end
@@ -189,18 +170,15 @@ describe Notify do
is_expected.to have_body_text 'Issue was moved to another project'
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains link to new issue' do
+ it 'has the correct subject and body' do
new_issue_url = namespace_project_issue_path(new_issue.project.namespace,
new_issue.project, new_issue)
- is_expected.to have_body_text new_issue_url
- end
- it 'contains a link to the original issue' do
- is_expected.to have_body_text namespace_project_issue_path(project.namespace, project, issue)
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(new_issue_url)
+ is_expected.to have_body_text(namespace_project_issue_path(project.namespace, project, issue))
+ end
end
end
end
@@ -220,20 +198,13 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request)
- end
-
- it 'contains a link to the new merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
- end
-
- it 'contains the source branch for the merge request' do
- is_expected.to have_body_text merge_request.source_branch
- end
-
- it 'contains the target branch for the merge request' do
- is_expected.to have_body_text merge_request.target_branch
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request)
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ is_expected.to have_body_text(merge_request.source_branch)
+ is_expected.to have_body_text(merge_request.target_branch)
+ end
end
context 'when enabled email_author_in_body' do
@@ -275,20 +246,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the name of the previous assignee' do
- is_expected.to have_html_escaped_body_text previous_assignee.name
- end
-
- it 'contains the name of the new assignee' do
- is_expected.to have_html_escaped_body_text assignee.name
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_html_escaped_body_text(previous_assignee.name)
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ is_expected.to have_html_escaped_body_text(assignee.name)
+ end
end
end
@@ -309,16 +273,10 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
+ it 'has the correct subject and body' do
is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the names of the added labels' do
- is_expected.to have_body_text 'foo, bar, and baz'
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ is_expected.to have_body_text('foo, bar, and baz')
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
end
end
@@ -338,20 +296,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the new status' do
- is_expected.to have_body_text status
- end
-
- it 'contains the user name' do
- is_expected.to have_html_escaped_body_text current_user.name
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text(status)
+ is_expected.to have_html_escaped_body_text(current_user.name)
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ end
end
end
@@ -371,16 +322,12 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains the new status' do
- is_expected.to have_body_text 'merged'
- end
-
- it 'contains a link to the merge request' do
- is_expected.to have_body_text namespace_project_merge_request_path(project.namespace, project, merge_request)
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text('merged')
+ is_expected.to have_body_text(namespace_project_merge_request_path(project.namespace, project, merge_request))
+ end
end
end
end
@@ -395,16 +342,10 @@ describe Notify do
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
- it 'has the correct subject' do
- is_expected.to have_subject "#{project.name} | Project was moved"
- end
-
- it 'contains name of project' do
+ it 'has the correct subject and body' do
+ is_expected.to have_subject("#{project.name} | Project was moved")
is_expected.to have_html_escaped_body_text project.name_with_namespace
- end
-
- it 'contains new user role' do
- is_expected.to have_body_text project.ssh_url_to_repo
+ is_expected.to have_body_text(project.ssh_url_to_repo)
end
end
@@ -597,14 +538,14 @@ describe Notify do
shared_examples 'a note email' do
it_behaves_like 'it should have Gmail Actions links'
- it 'is sent as the author' do
+ it 'is sent to the given recipient as the author' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(note_author.name)
- expect(sender.address).to eq(gitlab_sender)
- end
- it 'is sent to the given recipient' do
- is_expected.to deliver_to recipient.notification_email
+ aggregate_failures do
+ expect(sender.display_name).to eq(note_author.name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(subject).to deliver_to(recipient.notification_email)
+ end
end
it 'contains the message from the note' do
@@ -641,12 +582,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like 'a user cannot unsubscribe through footer link'
- it 'has the correct subject' do
- is_expected.to have_subject "Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})"
- end
-
- it 'contains a link to the commit' do
- is_expected.to have_body_text commit.short_id
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("Re: #{project.name} | #{commit.title.strip} (#{commit.short_id})")
+ is_expected.to have_body_text(commit.short_id)
+ end
end
end
@@ -664,12 +604,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(merge_request, reply: true)
- end
-
- it 'contains a link to the merge request note' do
- is_expected.to have_body_text note_on_merge_request_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(merge_request, reply: true)
+ is_expected.to have_body_text note_on_merge_request_path
+ end
end
end
@@ -687,12 +626,11 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
- it 'has the correct subject' do
- is_expected.to have_referable_subject(issue, reply: true)
- end
-
- it 'contains a link to the issue note' do
- is_expected.to have_body_text note_on_issue_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(note_on_issue_path)
+ end
end
end
end
@@ -717,14 +655,14 @@ describe Notify do
it_behaves_like 'it should have Gmail Actions links'
- it 'is sent as the author' do
+ it 'is sent to the given recipient as the author' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(note_author.name)
- expect(sender.address).to eq(gitlab_sender)
- end
- it 'is sent to the given recipient' do
- is_expected.to deliver_to recipient.notification_email
+ aggregate_failures do
+ expect(sender.display_name).to eq(note_author.name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(subject).to deliver_to(recipient.notification_email)
+ end
end
it 'contains the message from the note' do
@@ -934,21 +872,20 @@ describe Notify do
is_expected.to deliver_to 'new-email@mail.com'
end
- it 'has the correct subject' do
- is_expected.to have_subject 'Confirmation instructions | A Nice Suffix'
- end
-
- it 'includes a link to the site' do
- is_expected.to have_body_text example_site_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject('Confirmation instructions | A Nice Suffix')
+ is_expected.to have_body_text(example_site_path)
+ end
end
end
describe 'email on push for a created branch' do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
- let(:tree_path) { namespace_project_tree_path(project.namespace, project, "master") }
+ let(:tree_path) { namespace_project_tree_path(project.namespace, project, "empty-branch") }
- subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :create) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/empty-branch', action: :create) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like 'a user cannot unsubscribe through footer link'
@@ -961,12 +898,11 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}] Pushed new branch master"
- end
-
- it 'contains a link to the branch' do
- is_expected.to have_body_text tree_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}] Pushed new branch empty-branch")
+ is_expected.to have_body_text(tree_path)
+ end
end
end
@@ -988,12 +924,11 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}] Pushed new tag v1.0"
- end
-
- it 'contains a link to the tag' do
- is_expected.to have_body_text tree_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}] Pushed new tag v1.0")
+ is_expected.to have_body_text(tree_path)
+ end
end
end
@@ -1064,24 +999,14 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}][master] #{commits.length} commits: Ruby files modified"
- end
-
- it 'includes commits list' do
- is_expected.to have_body_text 'Change some files'
- end
-
- it 'includes diffs with character-level highlighting' do
- is_expected.to have_body_text 'def</span> <span class="nf">archive_formats_regex'
- end
-
- it 'contains a link to the diff' do
- is_expected.to have_body_text diff_path
- end
-
- it 'does not contain the misleading footer' do
- is_expected.not_to have_body_text 'you are a member of'
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}][master] #{commits.length} commits: Ruby files modified")
+ is_expected.to have_body_text('Change some files')
+ is_expected.to have_body_text('def</span> <span class="nf">archive_formats_regex')
+ is_expected.to have_body_text(diff_path)
+ is_expected.not_to have_body_text('you are a member of')
+ end
end
context "when set to send from committer email if domain matches" do
@@ -1098,13 +1023,13 @@ describe Notify do
end
it "is sent from the committer email" do
- sender = subject.header[:from].addrs[0]
- expect(sender.address).to eq(user.email)
- end
+ from = subject.header[:from].addrs.first
+ reply = subject.header[:reply_to].addrs.first
- it "is set to reply to the committer email" do
- sender = subject.header[:reply_to].addrs[0]
- expect(sender.address).to eq(user.email)
+ aggregate_failures do
+ expect(from.address).to eq(user.email)
+ expect(reply.address).to eq(user.email)
+ end
end
end
@@ -1115,13 +1040,13 @@ describe Notify do
end
it "is sent from the default email" do
- sender = subject.header[:from].addrs[0]
- expect(sender.address).to eq(gitlab_sender)
- end
+ from = subject.header[:from].addrs.first
+ reply = subject.header[:reply_to].addrs.first
- it "is set to reply to the default email" do
- sender = subject.header[:reply_to].addrs[0]
- expect(sender.address).to eq(gitlab_sender_reply_to)
+ aggregate_failures do
+ expect(from.address).to eq(gitlab_sender)
+ expect(reply.address).to eq(gitlab_sender_reply_to)
+ end
end
end
@@ -1132,13 +1057,13 @@ describe Notify do
end
it "is sent from the default email" do
- sender = subject.header[:from].addrs[0]
- expect(sender.address).to eq(gitlab_sender)
- end
+ from = subject.header[:from].addrs.first
+ reply = subject.header[:reply_to].addrs.first
- it "is set to reply to the default email" do
- sender = subject.header[:reply_to].addrs[0]
- expect(sender.address).to eq(gitlab_sender_reply_to)
+ aggregate_failures do
+ expect(from.address).to eq(gitlab_sender)
+ expect(reply.address).to eq(gitlab_sender_reply_to)
+ end
end
end
end
@@ -1166,20 +1091,13 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'has the correct subject' do
- is_expected.to have_subject "[Git][#{project.full_path}][master] #{commits.first.title}"
- end
-
- it 'includes commits list' do
- is_expected.to have_body_text 'Change some files'
- end
-
- it 'includes diffs with character-level highlighting' do
- is_expected.to have_body_text 'def</span> <span class="nf">archive_formats_regex'
- end
-
- it 'contains a link to the diff' do
- is_expected.to have_body_text diff_path
+ it 'has the correct subject and body' do
+ aggregate_failures do
+ is_expected.to have_subject("[Git][#{project.full_path}][master] #{commits.first.title}")
+ is_expected.to have_body_text('Change some files')
+ is_expected.to have_body_text('def</span> <span class="nf">archive_formats_regex')
+ is_expected.to have_body_text(diff_path)
+ end
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 9574796a945..4522206fab1 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -44,6 +44,34 @@ describe Issue, "Issuable" do
it { expect(described_class).to respond_to(:assigned) }
end
+ describe 'author_name' do
+ it 'is delegated to author' do
+ expect(issue.author_name).to eq issue.author.name
+ end
+
+ it 'returns nil when author is nil' do
+ issue.author_id = nil
+ issue.save(validate: false)
+
+ expect(issue.author_name).to eq nil
+ end
+ end
+
+ describe 'assignee_name' do
+ it 'is delegated to assignee' do
+ issue.update!(assignee: create(:user))
+
+ expect(issue.assignee_name).to eq issue.assignee.name
+ end
+
+ it 'returns nil when assignee is nil' do
+ issue.assignee_id = nil
+ issue.save(validate: false)
+
+ expect(issue.assignee_name).to eq nil
+ end
+ end
+
describe "before_save" do
describe "#update_cache_counts" do
context "when previous assignee exists" do
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index e8caad00c44..8acec805584 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -6,6 +6,9 @@ describe SystemHook, models: true do
let(:user) { create(:user) }
let(:project) { create(:empty_project, namespace: user.namespace) }
let(:group) { create(:group) }
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jg@example.com', password: 'mydummypass' }
+ end
before do
WebMock.stub_request(:post, system_hook.url)
@@ -29,7 +32,7 @@ describe SystemHook, models: true do
end
it "user_create hook" do
- create(:user)
+ Users::CreateService.new(nil, params).execute
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 73977d031f9..b8584301baa 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -670,4 +670,41 @@ describe Issue, models: true do
expect(attrs_hash).to include('time_estimate')
end
end
+
+ describe '#check_for_spam' do
+ let(:project) { create :project, visibility_level: visibility_level }
+ let(:issue) { create :issue, project: project }
+
+ subject do
+ issue.assign_attributes(description: description)
+ issue.check_for_spam?
+ end
+
+ context 'when project is public and spammable attributes changed' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
+ let(:description) { 'woo' }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when project is private' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
+ let(:description) { issue.description }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+
+ context 'when spammable attributes have not changed' do
+ let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }
+ let(:description) { issue.description }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 3cee2b7714f..f3f48f951a8 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -109,7 +109,7 @@ describe Milestone, models: true do
it { expect(milestone.percent_complete(user)).to eq(75) }
end
- describe :items_count do
+ describe '#is_empty?' do
before do
milestone.issues << create(:issue, project: project)
milestone.issues << create(:closed_issue, project: project)
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index bc70c6f4aa2..95f4785060c 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -165,7 +165,7 @@ describe Namespace, models: true do
end
end
- describe :rm_dir do
+ describe '#rm_dir', 'callback' do
let!(:project) { create(:empty_project, namespace: namespace) }
let!(:path) { File.join(Gitlab.config.repositories.storages.default['path'], namespace.full_path) }
@@ -217,10 +217,12 @@ describe Namespace, models: true do
end
describe '#descendants' do
- let!(:group) { create(:group) }
+ let!(:group) { create(:group, path: 'git_lab') }
let!(:nested_group) { create(:group, parent: group) }
let!(:deep_nested_group) { create(:group, parent: nested_group) }
let!(:very_deep_nested_group) { create(:group, parent: deep_nested_group) }
+ let!(:another_group) { create(:group, path: 'gitllab') }
+ let!(:another_group_nested) { create(:group, path: 'foo', parent: another_group) }
it 'returns the correct descendants' do
expect(very_deep_nested_group.descendants.to_a).to eq([])
diff --git a/spec/models/pages_domain_spec.rb b/spec/models/pages_domain_spec.rb
index e6a4583a8fb..c6c45d78990 100644
--- a/spec/models/pages_domain_spec.rb
+++ b/spec/models/pages_domain_spec.rb
@@ -5,7 +5,7 @@ describe PagesDomain, models: true do
it { is_expected.to belong_to(:project) }
end
- describe :validate_domain do
+ describe 'validate domain' do
subject { build(:pages_domain, domain: domain) }
context 'is unique' do
@@ -75,7 +75,7 @@ describe PagesDomain, models: true do
end
end
- describe :url do
+ describe '#url' do
subject { domain.url }
context 'without the certificate' do
@@ -91,7 +91,7 @@ describe PagesDomain, models: true do
end
end
- describe :has_matching_key? do
+ describe '#has_matching_key?' do
subject { domain.has_matching_key? }
context 'for matching key' do
@@ -107,7 +107,7 @@ describe PagesDomain, models: true do
end
end
- describe :has_intermediates? do
+ describe '#has_intermediates?' do
subject { domain.has_intermediates? }
context 'for self signed' do
@@ -133,7 +133,7 @@ describe PagesDomain, models: true do
end
end
- describe :expired? do
+ describe '#expired?' do
subject { domain.expired? }
context 'for valid' do
@@ -149,7 +149,7 @@ describe PagesDomain, models: true do
end
end
- describe :subject do
+ describe '#subject' do
let(:domain) { build(:pages_domain, :with_certificate) }
subject { domain.subject }
@@ -157,7 +157,7 @@ describe PagesDomain, models: true do
it { is_expected.to eq('/CN=test-certificate') }
end
- describe :certificate_text do
+ describe '#certificate_text' do
let(:domain) { build(:pages_domain, :with_certificate) }
subject { domain.certificate_text }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e4e75cc6a09..841c7d4cb5b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -1714,11 +1714,14 @@ describe Project, models: true do
end
describe 'inside_path' do
- let!(:project1) { create(:empty_project) }
+ let!(:project1) { create(:empty_project, namespace: create(:namespace, path: 'name_pace')) }
let!(:project2) { create(:empty_project) }
+ let!(:project3) { create(:empty_project, namespace: create(:namespace, path: 'namespace')) }
let!(:path) { project1.namespace.full_path }
- it { expect(Project.inside_path(path)).to eq([project1]) }
+ it 'returns correct project' do
+ expect(Project.inside_path(path)).to eq([project1])
+ end
end
describe '#route_map_for' do
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 274e4f00a0a..585b87b828d 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1083,7 +1083,7 @@ describe Repository, models: true do
end
end
- describe :skip_merged_commit do
+ describe 'skip_merges option' do
subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", limit: 100, skip_merges: true).map{ |k| k.id } }
it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') }
diff --git a/spec/models/route_spec.rb b/spec/models/route_spec.rb
index bc8ae4ae5a8..171a51fcc5b 100644
--- a/spec/models/route_spec.rb
+++ b/spec/models/route_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Route, models: true do
- let!(:group) { create(:group, path: 'gitlab', name: 'gitlab') }
+ let!(:group) { create(:group, path: 'git_lab', name: 'git_lab') }
let!(:route) { group.route }
describe 'relationships' do
@@ -14,10 +14,24 @@ describe Route, models: true do
it { is_expected.to validate_uniqueness_of(:path) }
end
+ describe '.inside_path' do
+ let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
+ let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
+ let!(:another_group) { create(:group, path: 'other') }
+ let!(:similar_group) { create(:group, path: 'gitllab') }
+ let!(:another_group_nested) { create(:group, path: 'another', name: 'another', parent: similar_group) }
+
+ it 'returns correct routes' do
+ expect(Route.inside_path('git_lab')).to match_array([nested_group.route, deep_nested_group.route])
+ end
+ end
+
describe '#rename_descendants' do
let!(:nested_group) { create(:group, path: 'test', name: 'test', parent: group) }
let!(:deep_nested_group) { create(:group, path: 'foo', name: 'foo', parent: nested_group) }
let!(:similar_group) { create(:group, path: 'gitlab-org', name: 'gitlab-org') }
+ let!(:another_group) { create(:group, path: 'gittlab', name: 'gitllab') }
+ let!(:another_group_nested) { create(:group, path: 'git_lab', name: 'git_lab', parent: another_group) }
context 'path update' do
context 'when route name is set' do
@@ -28,6 +42,8 @@ describe Route, models: true do
expect(described_class.exists?(path: 'bar/test')).to be_truthy
expect(described_class.exists?(path: 'bar/test/foo')).to be_truthy
expect(described_class.exists?(path: 'gitlab-org')).to be_truthy
+ expect(described_class.exists?(path: 'gittlab')).to be_truthy
+ expect(described_class.exists?(path: 'gittlab/git_lab')).to be_truthy
end
end
@@ -44,7 +60,7 @@ describe Route, models: true do
context 'name update' do
it "updates children routes with new path" do
- route.update_attributes(name: 'bar')
+ route.update_attributes(name: 'bar')
expect(described_class.exists?(name: 'bar')).to be_truthy
expect(described_class.exists?(name: 'bar / test')).to be_truthy
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 219ab1989ea..8095d01b69e 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -198,4 +198,47 @@ describe Snippet, models: true do
expect(snippet.participants).to include(note1.author, note2.author)
end
end
+
+ describe '#check_for_spam' do
+ let(:snippet) { create :snippet, visibility_level: visibility_level }
+
+ subject do
+ snippet.assign_attributes(title: title)
+ snippet.check_for_spam?
+ end
+
+ context 'when public and spammable attributes changed' do
+ let(:visibility_level) { Snippet::PUBLIC }
+ let(:title) { 'woo' }
+
+ it 'returns true' do
+ is_expected.to be_truthy
+ end
+ end
+
+ context 'when private' do
+ let(:visibility_level) { Snippet::PRIVATE }
+ let(:title) { snippet.title }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+
+ it 'returns true when switching to public' do
+ snippet.save!
+ snippet.visibility_level = Snippet::PUBLIC
+
+ expect(snippet.check_for_spam?).to be_truthy
+ end
+ end
+
+ context 'when spammable attributes have not changed' do
+ let(:visibility_level) { Snippet::PUBLIC }
+ let(:title) { snippet.title }
+
+ it 'returns false' do
+ is_expected.to be_falsey
+ end
+ end
+ end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 90378179e32..a9e37be1157 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -81,6 +81,7 @@ describe User, models: true do
it { is_expected.to validate_numericality_of(:projects_limit) }
it { is_expected.to allow_value(0).for(:projects_limit) }
it { is_expected.not_to allow_value(-1).for(:projects_limit) }
+ it { is_expected.not_to allow_value(Gitlab::Database::MAX_INT_VALUE + 1).for(:projects_limit) }
it { is_expected.to validate_length_of(:bio).is_at_most(255) }
@@ -360,22 +361,10 @@ describe User, models: true do
end
describe '#generate_password' do
- it "executes callback when force_random_password specified" do
- user = build(:user, force_random_password: true)
- expect(user).to receive(:generate_password)
- user.save
- end
-
it "does not generate password by default" do
user = create(:user, password: 'abcdefghe')
expect(user.password).to eq('abcdefghe')
end
-
- it "generates password when forcing random password" do
- allow(Devise).to receive(:friendly_token).and_return('123456789')
- user = create(:user, password: 'abcdefg', force_random_password: true)
- expect(user.password).to eq('12345678')
- end
end
describe 'authentication token' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index c481b7e72b1..a3de4702ad0 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -902,7 +902,7 @@ describe API::Projects, :api do
end
end
- describe :fork_admin do
+ describe 'fork management' do
let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:empty_project, :public) }
diff --git a/spec/requests/api/v3/projects_spec.rb b/spec/requests/api/v3/projects_spec.rb
index d8bb562587d..b1aa793ec00 100644
--- a/spec/requests/api/v3/projects_spec.rb
+++ b/spec/requests/api/v3/projects_spec.rb
@@ -949,7 +949,7 @@ describe API::V3::Projects, api: true do
end
end
- describe :fork_admin do
+ describe 'fork management' do
let(:project_fork_target) { create(:empty_project) }
let(:project_fork_source) { create(:empty_project, :public) }
diff --git a/spec/requests/api/v3/users_spec.rb b/spec/requests/api/v3/users_spec.rb
index 17bbb0b53c1..b38cbe74b85 100644
--- a/spec/requests/api/v3/users_spec.rb
+++ b/spec/requests/api/v3/users_spec.rb
@@ -263,4 +263,18 @@ describe API::V3::Users, api: true do
expect(json_response['message']).to eq('404 User Not Found')
end
end
+
+ describe 'POST /users' do
+ it 'creates confirmed user when confirm parameter is false' do
+ optional_attributes = { confirm: false }
+ attributes = attributes_for(:user).merge(optional_attributes)
+
+ post v3_api('/users', admin), attributes
+
+ user_id = json_response['id']
+ new_user = User.find(user_id)
+
+ expect(new_user).to be_confirmed
+ end
+ end
end
diff --git a/spec/services/boards/issues/list_service_spec.rb b/spec/services/boards/issues/list_service_spec.rb
index 22115c6566d..d841bdaa292 100644
--- a/spec/services/boards/issues/list_service_spec.rb
+++ b/spec/services/boards/issues/list_service_spec.rb
@@ -30,6 +30,7 @@ describe Boards::Issues::ListService, services: true do
let!(:closed_issue2) { create(:labeled_issue, :closed, project: project, labels: [p3]) }
let!(:closed_issue3) { create(:issue, :closed, project: project) }
let!(:closed_issue4) { create(:labeled_issue, :closed, project: project, labels: [p1]) }
+ let!(:closed_issue5) { create(:labeled_issue, :closed, project: project, labels: [development]) }
before do
project.team << [user, :developer]
@@ -57,7 +58,7 @@ describe Boards::Issues::ListService, services: true do
issues = described_class.new(project, user, params).execute
- expect(issues).to eq [closed_issue4, closed_issue2, closed_issue3, closed_issue1]
+ expect(issues).to eq [closed_issue4, closed_issue2, closed_issue5, closed_issue3, closed_issue1]
end
it 'returns opened issues that have label list applied when listing issues from a label list' do
diff --git a/spec/services/create_branch_service_spec.rb b/spec/services/create_branch_service_spec.rb
new file mode 100644
index 00000000000..3f548688c20
--- /dev/null
+++ b/spec/services/create_branch_service_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe CreateBranchService, services: true do
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(project, user) }
+
+ describe '#execute' do
+ context 'when repository is empty' do
+ let(:project) { create(:project_empty_repo) }
+
+ it 'creates master branch' do
+ service.execute('my-feature', 'master')
+
+ expect(project.repository.branch_exists?('master')).to be_truthy
+ end
+
+ it 'creates my-feature branch' do
+ service.execute('my-feature', 'master')
+
+ expect(project.repository.branch_exists?('my-feature')).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/services/issuable/bulk_update_service_spec.rb b/spec/services/issuable/bulk_update_service_spec.rb
index 0475f38fe5e..7a1ac027310 100644
--- a/spec/services/issuable/bulk_update_service_spec.rb
+++ b/spec/services/issuable/bulk_update_service_spec.rb
@@ -138,7 +138,7 @@ describe Issuable::BulkUpdateService, services: true do
let(:labels) { [bug, regression] }
it 'updates the labels of all issues passed to the labels passed' do
- expect(issues.map(&:reload).map(&:label_ids)).to all(eq(labels.map(&:id)))
+ expect(issues.map(&:reload).map(&:label_ids)).to all(match_array(labels.map(&:id)))
end
it 'does not update issues not passed in' do
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
index 92b84308f73..fe6a19e97ea 100644
--- a/spec/services/milestones/close_service_spec.rb
+++ b/spec/services/milestones/close_service_spec.rb
@@ -17,7 +17,7 @@ describe Milestones::CloseService, services: true do
it { expect(milestone).to be_valid }
it { expect(milestone).to be_closed }
- describe :event do
+ describe 'event' do
let(:event) { Event.recent.first }
it { expect(event.milestone).to be_truthy }
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 8e614211116..e3be1989c93 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe Projects::ForkService, services: true do
- describe :fork_by_user do
+ describe 'fork by user' do
before do
@from_namespace = create(:namespace)
@from_user = create(:user, namespace: @from_namespace )
@@ -100,7 +100,7 @@ describe Projects::ForkService, services: true do
end
end
- describe :fork_to_namespace do
+ describe 'fork to namespace' do
before do
@group_owner = create(:user)
@developer = create(:user)
diff --git a/spec/services/spam_service_spec.rb b/spec/services/spam_service_spec.rb
index 4ce3b95aa87..e09c05ccf32 100644
--- a/spec/services/spam_service_spec.rb
+++ b/spec/services/spam_service_spec.rb
@@ -19,42 +19,67 @@ describe SpamService, services: true do
let(:issue) { create(:issue, project: project) }
let(:request) { double(:request, env: {}) }
- context 'when indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: true)) }
+ context 'when spammable attributes have not changed' do
+ before do
+ issue.closed_at = Time.zone.now
- it 'doesnt check as spam when request is missing' do
- check_spam(issue, nil, false)
-
- expect(issue.spam).to be_falsey
+ allow(AkismetService).to receive(:new).and_return(double(is_spam?: true))
end
- it 'checks as spam' do
- check_spam(issue, request, false)
-
- expect(issue.spam).to be_truthy
+ it 'returns false' do
+ expect(check_spam(issue, request, false)).to be_falsey
end
- it 'creates a spam log' do
+ it 'does not create a spam log' do
expect { check_spam(issue, request, false) }
- .to change { SpamLog.count }.from(0).to(1)
+ .not_to change { SpamLog.count }
end
+ end
- it 'doesnt yield block' do
- expect(check_spam(issue, request, false))
- .to eql(SpamLog.last)
+ context 'when spammable attributes have changed' do
+ before do
+ issue.description = 'SPAM!'
end
- end
- context 'when not indicated as spam by akismet' do
- before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+ context 'when indicated as spam by akismet' do
+ before do
+ allow(AkismetService).to receive(:new).and_return(double(is_spam?: true))
+ end
- it 'returns false' do
- expect(check_spam(issue, request, false)).to be_falsey
+ it 'doesnt check as spam when request is missing' do
+ check_spam(issue, nil, false)
+
+ expect(issue.spam).to be_falsey
+ end
+
+ it 'checks as spam' do
+ check_spam(issue, request, false)
+
+ expect(issue.spam).to be_truthy
+ end
+
+ it 'creates a spam log' do
+ expect { check_spam(issue, request, false) }
+ .to change { SpamLog.count }.from(0).to(1)
+ end
+
+ it 'doesnt yield block' do
+ expect(check_spam(issue, request, false))
+ .to eql(SpamLog.last)
+ end
end
- it 'does not create a spam log' do
- expect { check_spam(issue, request, false) }
- .not_to change { SpamLog.count }
+ context 'when not indicated as spam by akismet' do
+ before { allow(AkismetService).to receive(:new).and_return(double(is_spam?: false)) }
+
+ it 'returns false' do
+ expect(check_spam(issue, request, false)).to be_falsey
+ end
+
+ it 'does not create a spam log' do
+ expect { check_spam(issue, request, false) }
+ .not_to change { SpamLog.count }
+ end
end
end
end
diff --git a/spec/services/users/create_service_spec.rb b/spec/services/users/create_service_spec.rb
new file mode 100644
index 00000000000..5f79203701a
--- /dev/null
+++ b/spec/services/users/create_service_spec.rb
@@ -0,0 +1,182 @@
+require 'spec_helper'
+
+describe Users::CreateService, services: true do
+ describe '#build' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' }
+ end
+
+ context 'with an admin user' do
+ let(:admin_user) { create(:admin) }
+ let(:service) { described_class.new(admin_user, params) }
+
+ it 'returns a valid user' do
+ expect(service.build).to be_valid
+ end
+ end
+
+ context 'with non admin user' do
+ let(:user) { create(:user) }
+ let(:service) { described_class.new(user, params) }
+
+ it 'raises AccessDeniedError exception' do
+ expect { service.build }.to raise_error Gitlab::Access::AccessDeniedError
+ end
+ end
+
+ context 'with nil user' do
+ let(:service) { described_class.new(nil, params) }
+
+ it 'returns a valid user' do
+ expect(service.build).to be_valid
+ end
+ end
+ end
+
+ describe '#execute' do
+ let(:admin_user) { create(:admin) }
+
+ context 'with an admin user' do
+ let(:service) { described_class.new(admin_user, params) }
+
+ context 'when required parameters are provided' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass' }
+ end
+
+ it 'returns a persisted user' do
+ expect(service.execute).to be_persisted
+ end
+
+ it 'persists the given attributes' do
+ user = service.execute
+ user.reload
+
+ expect(user).to have_attributes(
+ name: params[:name],
+ username: params[:username],
+ email: params[:email],
+ password: params[:password],
+ created_by_id: admin_user.id
+ )
+ end
+
+ it 'user is not confirmed if skip_confirmation param is not present' do
+ expect(service.execute).not_to be_confirmed
+ end
+
+ it 'logs the user creation' do
+ expect(service).to receive(:log_info).with("User \"John Doe\" (jd@example.com) was created")
+
+ service.execute
+ end
+
+ it 'executes system hooks ' do
+ system_hook_service = spy(:system_hook_service)
+
+ expect(service).to receive(:system_hook_service).and_return(system_hook_service)
+
+ user = service.execute
+
+ expect(system_hook_service).to have_received(:execute_hooks_for).with(user, :create)
+ end
+
+ it 'does not send a notification email' do
+ notification_service = spy(:notification_service)
+
+ expect(service).not_to receive(:notification_service)
+
+ service.execute
+
+ expect(notification_service).not_to have_received(:new_user)
+ end
+ end
+
+ context 'when force_random_password parameter is true' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', force_random_password: true }
+ end
+
+ it 'generates random password' do
+ user = service.execute
+
+ expect(user.password).not_to eq 'mydummypass'
+ expect(user.password).to be_present
+ end
+ end
+
+ context 'when skip_confirmation parameter is true' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', skip_confirmation: true }
+ end
+
+ it 'confirms the user' do
+ expect(service.execute).to be_confirmed
+ end
+ end
+
+ context 'when reset_password parameter is true' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', reset_password: true }
+ end
+
+ it 'resets password even if a password parameter is given' do
+ expect(service.execute).to be_recently_sent_password_reset
+ end
+
+ it 'sends a notification email' do
+ notification_service = spy(:notification_service)
+
+ expect(service).to receive(:notification_service).and_return(notification_service)
+
+ user = service.execute
+
+ expect(notification_service).to have_received(:new_user).with(user, an_instance_of(String))
+ end
+ end
+ end
+
+ context 'with nil user' do
+ let(:params) do
+ { name: 'John Doe', username: 'jduser', email: 'jd@example.com', password: 'mydummypass', skip_confirmation: true }
+ end
+ let(:service) { described_class.new(nil, params) }
+
+ context 'when "send_user_confirmation_email" application setting is true' do
+ before do
+ current_application_settings = double(:current_application_settings, send_user_confirmation_email: true, signup_enabled?: true)
+ allow(service).to receive(:current_application_settings).and_return(current_application_settings)
+ end
+
+ it 'does not confirm the user' do
+ expect(service.execute).not_to be_confirmed
+ end
+ end
+
+ context 'when "send_user_confirmation_email" application setting is false' do
+ before do
+ current_application_settings = double(:current_application_settings, send_user_confirmation_email: false, signup_enabled?: true)
+ allow(service).to receive(:current_application_settings).and_return(current_application_settings)
+ end
+
+ it 'confirms the user' do
+ expect(service.execute).to be_confirmed
+ end
+
+ it 'persists the given attributes' do
+ user = service.execute
+ user.reload
+
+ expect(user).to have_attributes(
+ name: params[:name],
+ username: params[:username],
+ email: params[:email],
+ password: params[:password],
+ created_by_id: nil,
+ admin: false
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ceb3209331f..5ab8f0d981a 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -35,7 +35,8 @@ RSpec.configure do |config|
config.include Warden::Test::Helpers, type: :request
config.include LoginHelpers, type: :feature
config.include SearchHelpers, type: :feature
- config.include WaitForAjax, type: :feature
+ config.include WaitForRequests, :js
+ config.include WaitForAjax, :js
config.include StubConfiguration
config.include EmailHelpers, type: :mailer
config.include TestEnv
diff --git a/spec/support/notify_shared_examples.rb b/spec/support/notify_shared_examples.rb
index a3724b801b3..16a425f2ca2 100644
--- a/spec/support/notify_shared_examples.rb
+++ b/spec/support/notify_shared_examples.rb
@@ -27,24 +27,14 @@ shared_examples 'a multiple recipients email' do
end
shared_examples 'an email sent from GitLab' do
- it 'is sent from GitLab' do
+ it 'has the characteristics of an email sent from GitLab' do
sender = subject.header[:from].addrs[0]
- expect(sender.display_name).to eq(gitlab_sender_display_name)
- expect(sender.address).to eq(gitlab_sender)
- end
-
- it 'has a Reply-To address' do
reply_to = subject.header[:reply_to].addresses
- expect(reply_to).to eq([gitlab_sender_reply_to])
- end
-
- context 'when custom suffix for email subject is set' do
- before do
- stub_config_setting(email_subject_suffix: 'A Nice Suffix')
- end
- it 'ends the subject with the suffix' do
- is_expected.to have_subject /\ \| A Nice Suffix$/
+ aggregate_failures do
+ expect(sender.display_name).to eq(gitlab_sender_display_name)
+ expect(sender.address).to eq(gitlab_sender)
+ expect(reply_to).to eq([gitlab_sender_reply_to])
end
end
end
@@ -56,43 +46,40 @@ shared_examples 'an email that contains a header with author username' do
end
shared_examples 'an email with X-GitLab headers containing project details' do
- it 'has X-GitLab-Project* headers' do
- is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
- is_expected.to have_header 'X-GitLab-Project-Id', /#{project.id}/
- is_expected.to have_header 'X-GitLab-Project-Path', /#{project.path_with_namespace}/
+ it 'has X-GitLab-Project headers' do
+ aggregate_failures do
+ is_expected.to have_header('X-GitLab-Project', /#{project.name}/)
+ is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/)
+ is_expected.to have_header('X-GitLab-Project-Path', /#{project.path_with_namespace}/)
+ end
end
end
shared_examples 'a new thread email with reply-by-email enabled' do
- let(:regex) { /\A<reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
-
- it 'has a Message-ID header' do
- is_expected.to have_header 'Message-ID', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
- end
+ it 'has the characteristics of a threaded email' do
+ host = Gitlab.config.gitlab.host
+ route_key = "#{model.class.model_name.singular_route_key}_#{model.id}"
- it 'has a References header' do
- is_expected.to have_header 'References', regex
+ aggregate_failures do
+ is_expected.to have_header('Message-ID', "<#{route_key}@#{host}>")
+ is_expected.to have_header('References', /\A<reply\-.*@#{host}>\Z/ )
+ end
end
end
shared_examples 'a thread answer email with reply-by-email enabled' do
include_examples 'an email with X-GitLab headers containing project details'
- let(:regex) { /\A<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}> <reply\-(.*)@#{Gitlab.config.gitlab.host}>\Z/ }
-
- it 'has a Message-ID header' do
- is_expected.to have_header 'Message-ID', /\A<(.*)@#{Gitlab.config.gitlab.host}>\Z/
- end
-
- it 'has a In-Reply-To header' do
- is_expected.to have_header 'In-Reply-To', "<#{model.class.model_name.singular_route_key}_#{model.id}@#{Gitlab.config.gitlab.host}>"
- end
- it 'has a References header' do
- is_expected.to have_header 'References', regex
- end
+ it 'has the characteristics of a threaded reply' do
+ host = Gitlab.config.gitlab.host
+ route_key = "#{model.class.model_name.singular_route_key}_#{model.id}"
- it 'has a subject that begins with Re: ' do
- is_expected.to have_subject /^Re: /
+ aggregate_failures do
+ is_expected.to have_header('Message-ID', /\A<.*@#{host}>\Z/)
+ is_expected.to have_header('In-Reply-To', "<#{route_key}@#{host}>")
+ is_expected.to have_header('References', /\A<#{route_key}@#{host}> <reply\-.*@#{host}>\Z/ )
+ is_expected.to have_subject(/^Re: /)
+ end
end
end
@@ -136,80 +123,77 @@ shared_examples 'an answer to an existing thread with reply-by-email enabled' do
end
end
-shared_examples 'a new user email' do
- it 'is sent to the new user' do
- is_expected.to deliver_to new_user_address
- end
-
- it 'has the correct subject' do
- is_expected.to have_subject /^Account was created for you$/i
- end
-
- it 'contains the new user\'s login name' do
- is_expected.to have_body_text /#{new_user_address}/
- end
-end
-
shared_examples 'it should have Gmail Actions links' do
- it { is_expected.to have_body_text '<script type="application/ld+json">' }
- it { is_expected.to have_body_text /ViewAction/ }
+ it do
+ aggregate_failures do
+ is_expected.to have_body_text('<script type="application/ld+json">')
+ is_expected.to have_body_text('ViewAction')
+ end
+ end
end
shared_examples 'it should not have Gmail Actions links' do
- it { is_expected.not_to have_body_text '<script type="application/ld+json">' }
- it { is_expected.not_to have_body_text /ViewAction/ }
+ it do
+ aggregate_failures do
+ is_expected.not_to have_body_text('<script type="application/ld+json">')
+ is_expected.not_to have_body_text('ViewAction')
+ end
+ end
end
shared_examples 'it should show Gmail Actions View Issue link' do
it_behaves_like 'it should have Gmail Actions links'
- it { is_expected.to have_body_text /View Issue/ }
+ it { is_expected.to have_body_text('View Issue') }
end
shared_examples 'it should show Gmail Actions View Merge request link' do
it_behaves_like 'it should have Gmail Actions links'
- it { is_expected.to have_body_text /View Merge request/ }
+ it { is_expected.to have_body_text('View Merge request') }
end
shared_examples 'it should show Gmail Actions View Commit link' do
it_behaves_like 'it should have Gmail Actions links'
- it { is_expected.to have_body_text /View Commit/ }
+ it { is_expected.to have_body_text('View Commit') }
end
shared_examples 'an unsubscribeable thread' do
it_behaves_like 'an unsubscribeable thread with incoming address without %{key}'
- it 'has a List-Unsubscribe header in the correct format' do
- is_expected.to have_header 'List-Unsubscribe', /unsubscribe/
- is_expected.to have_header 'List-Unsubscribe', /mailto/
- is_expected.to have_header 'List-Unsubscribe', /^<.+,.+>$/
+ it 'has a List-Unsubscribe header in the correct format, and a body link' do
+ aggregate_failures do
+ is_expected.to have_header('List-Unsubscribe', /unsubscribe/)
+ is_expected.to have_header('List-Unsubscribe', /mailto/)
+ is_expected.to have_header('List-Unsubscribe', /^<.+,.+>$/)
+ is_expected.to have_body_text('unsubscribe')
+ end
end
-
- it { is_expected.to have_body_text /unsubscribe/ }
end
shared_examples 'an unsubscribeable thread with incoming address without %{key}' do
include_context 'reply-by-email is enabled with incoming address without %{key}'
- it 'has a List-Unsubscribe header in the correct format' do
- is_expected.to have_header 'List-Unsubscribe', /unsubscribe/
- is_expected.not_to have_header 'List-Unsubscribe', /mailto/
- is_expected.to have_header 'List-Unsubscribe', /^<[^,]+>$/
+ it 'has a List-Unsubscribe header in the correct format, and a body link' do
+ aggregate_failures do
+ is_expected.to have_header('List-Unsubscribe', /unsubscribe/)
+ is_expected.not_to have_header('List-Unsubscribe', /mailto/)
+ is_expected.to have_header('List-Unsubscribe', /^<[^,]+>$/)
+ is_expected.to have_body_text('unsubscribe')
+ end
end
-
- it { is_expected.to have_body_text /unsubscribe/ }
end
shared_examples 'a user cannot unsubscribe through footer link' do
- it 'does not have a List-Unsubscribe header' do
- is_expected.not_to have_header 'List-Unsubscribe', /unsubscribe/
+ it 'does not have a List-Unsubscribe header or a body link' do
+ aggregate_failures do
+ is_expected.not_to have_header('List-Unsubscribe', /unsubscribe/)
+ is_expected.not_to have_body_text('unsubscribe')
+ end
end
-
- it { is_expected.not_to have_body_text /unsubscribe/ }
end
shared_examples 'an email with a labels subscriptions link in its footer' do
- it { is_expected.to have_body_text /label subscriptions/ }
+ it { is_expected.to have_body_text('label subscriptions') }
end
diff --git a/spec/support/prometheus_helpers.rb b/spec/support/prometheus_helpers.rb
index a52d8f37d14..4afdbd68304 100644
--- a/spec/support/prometheus_helpers.rb
+++ b/spec/support/prometheus_helpers.rb
@@ -1,10 +1,10 @@
module PrometheusHelpers
def prometheus_memory_query(environment_slug)
- %{sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})/1024/1024}
+ %{(sum(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"}) / count(container_memory_usage_bytes{container_name="app",environment="#{environment_slug}"})) /1024/1024}
end
def prometheus_cpu_query(environment_slug)
- %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m]))}
+ %{sum(rate(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}[2m])) / count(container_cpu_usage_seconds_total{container_name="app",environment="#{environment_slug}"}) * 100}
end
def prometheus_query_url(prometheus_query)
diff --git a/spec/support/slack_mattermost_notifications_shared_examples.rb b/spec/support/slack_mattermost_notifications_shared_examples.rb
index 704922b6cf4..b902fe90707 100644
--- a/spec/support/slack_mattermost_notifications_shared_examples.rb
+++ b/spec/support/slack_mattermost_notifications_shared_examples.rb
@@ -324,5 +324,24 @@ RSpec.shared_examples 'slack or mattermost notifications' do
it_behaves_like 'call Slack/Mattermost API'
end
end
+
+ context 'only notify for the default branch' do
+ context 'when enabled' do
+ let(:pipeline) do
+ create(:ci_pipeline, project: project, status: 'failed', ref: 'not-the-default-branch')
+ end
+
+ before do
+ chat_service.notify_only_default_branch = true
+ end
+
+ it 'does not call the Slack/Mattermost API for pipeline events' do
+ data = Gitlab::DataBuilder::Pipeline.build(pipeline)
+ result = chat_service.execute(data)
+
+ expect(result).to be_falsy
+ end
+ end
+ end
end
end
diff --git a/spec/support/target_branch_helpers.rb b/spec/support/target_branch_helpers.rb
new file mode 100644
index 00000000000..3ee8f0f657e
--- /dev/null
+++ b/spec/support/target_branch_helpers.rb
@@ -0,0 +1,16 @@
+module TargetBranchHelpers
+ def select_branch(name)
+ first('button.js-target-branch').click
+ wait_for_ajax
+ all('a[data-group="Branches"]').find do |el|
+ el.text == name
+ end.click
+ end
+
+ def create_new_branch(name)
+ first('button.js-target-branch').click
+ click_link 'Create new branch'
+ fill_in 'new_branch_name', with: name
+ click_button 'Create'
+ end
+end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index f1d226b6ae3..648b0380f18 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -37,9 +37,10 @@ module TestEnv
'conflict-too-large' => '39fa04f',
'deleted-image-test' => '6c17798',
'wip' => 'b9238ee',
- 'csv' => '3dd0896'
+ 'csv' => '3dd0896',
+ 'v1.1.0' => 'b83d6e3'
}.freeze
-
+
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
# need to keep all the branches in sync.
# We currently only need a subset of the branches
diff --git a/spec/support/wait_for_requests.rb b/spec/support/wait_for_requests.rb
new file mode 100644
index 00000000000..0bfa7f72ff8
--- /dev/null
+++ b/spec/support/wait_for_requests.rb
@@ -0,0 +1,32 @@
+module WaitForRequests
+ extend self
+
+ # This is inspired by http://www.salsify.com/blog/engineering/tearing-capybara-ajax-tests
+ def wait_for_requests_complete
+ Gitlab::Testing::RequestBlockerMiddleware.block_requests!
+ wait_for('pending AJAX requests complete') do
+ Gitlab::Testing::RequestBlockerMiddleware.num_active_requests.zero?
+ end
+ ensure
+ Gitlab::Testing::RequestBlockerMiddleware.allow_requests!
+ end
+
+ # Waits until the passed block returns true
+ def wait_for(condition_name, max_wait_time: Capybara.default_max_wait_time, polling_interval: 0.01)
+ wait_until = Time.now + max_wait_time.seconds
+ loop do
+ break if yield
+ if Time.now > wait_until
+ raise "Condition not met: #{condition_name}"
+ else
+ sleep(polling_interval)
+ end
+ end
+ end
+end
+
+RSpec.configure do |config|
+ config.after(:each, :js) do
+ wait_for_requests_complete
+ end
+end
diff --git a/spec/support/wait_for_vue_resource.rb b/spec/support/wait_for_vue_resource.rb
index 1029f84716f..4a4e2e16ee7 100644
--- a/spec/support/wait_for_vue_resource.rb
+++ b/spec/support/wait_for_vue_resource.rb
@@ -1,7 +1,7 @@
module WaitForVueResource
def wait_for_vue_resource(spinner: true)
Timeout.timeout(Capybara.default_max_wait_time) do
- loop until page.evaluate_script('Vue.activeResources').zero?
+ loop until page.evaluate_script('window.activeVueResources').zero?
end
end
end
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
new file mode 100644
index 00000000000..d95baddf546
--- /dev/null
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -0,0 +1,78 @@
+require 'rake_helper'
+
+describe 'gitlab:gitaly namespace rake task' do
+ before :all do
+ Rake.application.rake_require 'tasks/gitlab/gitaly'
+ end
+
+ describe 'install' do
+ let(:repo) { 'https://gitlab.com/gitlab-org/gitaly.git' }
+ let(:clone_path) { Rails.root.join('tmp/tests/gitaly').to_s }
+ let(:tag) { "v#{File.read(Rails.root.join(Gitlab::GitalyClient::SERVER_VERSION_FILE)).chomp}" }
+
+ context 'no dir given' do
+ it 'aborts and display a help message' do
+ # avoid writing task output to spec progress
+ allow($stderr).to receive :write
+ expect { run_rake_task('gitlab:gitaly:install') }.to raise_error /Please specify the directory where you want to install gitaly/
+ end
+ end
+
+ context 'when an underlying Git command fail' do
+ it 'aborts and display a help message' do
+ expect_any_instance_of(Object).
+ to receive(:checkout_or_clone_tag).and_raise 'Git error'
+
+ expect { run_rake_task('gitlab:gitaly:install', clone_path) }.to raise_error 'Git error'
+ end
+ end
+
+ describe 'checkout or clone' do
+ before do
+ expect(Dir).to receive(:chdir).with(clone_path)
+ end
+
+ it 'calls checkout_or_clone_tag with the right arguments' do
+ expect_any_instance_of(Object).
+ to receive(:checkout_or_clone_tag).with(tag: tag, repo: repo, target_dir: clone_path)
+
+ run_rake_task('gitlab:gitaly:install', clone_path)
+ end
+ end
+
+ describe 'gmake/make' do
+ before do
+ FileUtils.mkdir_p(clone_path)
+ expect(Dir).to receive(:chdir).with(clone_path).and_call_original
+ end
+
+ context 'gmake is available' do
+ before do
+ expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
+ allow_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+ end
+
+ it 'calls gmake in the gitaly directory' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['/usr/bin/gmake', 0])
+ expect_any_instance_of(Object).to receive(:run_command!).with(['gmake']).and_return(true)
+
+ run_rake_task('gitlab:gitaly:install', clone_path)
+ end
+ end
+
+ context 'gmake is not available' do
+ before do
+ expect_any_instance_of(Object).to receive(:checkout_or_clone_tag)
+ allow_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+ end
+
+ it 'calls make in the gitaly directory' do
+ expect(Gitlab::Popen).to receive(:popen).with(%w[which gmake]).and_return(['', 42])
+ expect_any_instance_of(Object).to receive(:run_command!).with(['make']).and_return(true)
+
+ run_rake_task('gitlab:gitaly:install', clone_path)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/workhorse_rake_spec.rb b/spec/tasks/gitlab/workhorse_rake_spec.rb
index 6de66c3cf07..8a66a4aa047 100644
--- a/spec/tasks/gitlab/workhorse_rake_spec.rb
+++ b/spec/tasks/gitlab/workhorse_rake_spec.rb
@@ -9,9 +9,6 @@ describe 'gitlab:workhorse namespace rake task' do
let(:repo) { 'https://gitlab.com/gitlab-org/gitlab-workhorse.git' }
let(:clone_path) { Rails.root.join('tmp/tests/gitlab-workhorse').to_s }
let(:tag) { "v#{File.read(Rails.root.join(Gitlab::Workhorse::VERSION_FILE)).chomp}" }
- before do
- allow(ENV).to receive(:[])
- end
context 'no dir given' do
it 'aborts and display a help message' do
diff --git a/spec/tasks/tokens_spec.rb b/spec/tasks/tokens_spec.rb
new file mode 100644
index 00000000000..19036c7677c
--- /dev/null
+++ b/spec/tasks/tokens_spec.rb
@@ -0,0 +1,21 @@
+require 'rake_helper'
+
+describe 'tokens rake tasks' do
+ let!(:user) { create(:user) }
+
+ before do
+ Rake.application.rake_require 'tasks/tokens'
+ end
+
+ describe 'reset_all task' do
+ it 'invokes create_hooks task' do
+ expect { run_rake_task('tokens:reset_all_auth') }.to change { user.reload.authentication_token }
+ end
+ end
+
+ describe 'reset_all_email task' do
+ it 'invokes create_hooks task' do
+ expect { run_rake_task('tokens:reset_all_email') }.to change { user.reload.incoming_email_token }
+ end
+ end
+end
diff --git a/spec/views/projects/pipelines/show.html.haml_spec.rb b/spec/views/projects/pipelines/show.html.haml_spec.rb
index c101f6f164d..e4aeaeca508 100644
--- a/spec/views/projects/pipelines/show.html.haml_spec.rb
+++ b/spec/views/projects/pipelines/show.html.haml_spec.rb
@@ -3,8 +3,9 @@ require 'spec_helper'
describe 'projects/pipelines/show' do
include Devise::Test::ControllerHelpers
+ let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id) }
+ let(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, user: user) }
before do
controller.prepend_view_path('app/views/projects')
@@ -21,6 +22,7 @@ describe 'projects/pipelines/show' do
assign(:project, project)
assign(:pipeline, pipeline)
+ assign(:commit, project.commit)
allow(view).to receive(:can?).and_return(true)
end
@@ -31,6 +33,12 @@ describe 'projects/pipelines/show' do
expect(rendered).to have_css('.js-pipeline-graph')
expect(rendered).to have_css('.js-grouped-pipeline-dropdown')
+ # header
+ expect(rendered).to have_text("##{pipeline.id}")
+ expect(rendered).to have_css('time', text: pipeline.created_at.strftime("%b %d, %Y"))
+ expect(rendered).to have_selector(%Q(img[alt$="#{pipeline.user.name}'s avatar"]))
+ expect(rendered).to have_link(pipeline.user.name, href: user_path(pipeline.user))
+
# stages
expect(rendered).to have_text('Build')
expect(rendered).to have_text('Test')
diff --git a/tmp/sockets/private/.gitkeep b/tmp/sockets/private/.gitkeep
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/tmp/sockets/private/.gitkeep
diff --git a/vendor/assets/javascripts/js.cookie.js b/vendor/assets/javascripts/js.cookie.js
deleted file mode 100644
index 92dbba162c4..00000000000
--- a/vendor/assets/javascripts/js.cookie.js
+++ /dev/null
@@ -1,156 +0,0 @@
-/*!
- * JavaScript Cookie v2.1.3
- * https://github.com/js-cookie/js-cookie
- *
- * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
- * Released under the MIT license
- */
-;(function (factory) {
- var registeredInModuleLoader = false;
- if (typeof define === 'function' && define.amd) {
- define(factory);
- registeredInModuleLoader = true;
- }
- if (typeof exports === 'object') {
- module.exports = factory();
- registeredInModuleLoader = true;
- }
- if (!registeredInModuleLoader) {
- var OldCookies = window.Cookies;
- var api = window.Cookies = factory();
- api.noConflict = function () {
- window.Cookies = OldCookies;
- return api;
- };
- }
-}(function () {
- function extend () {
- var i = 0;
- var result = {};
- for (; i < arguments.length; i++) {
- var attributes = arguments[ i ];
- for (var key in attributes) {
- result[key] = attributes[key];
- }
- }
- return result;
- }
-
- function init (converter) {
- function api (key, value, attributes) {
- var result;
- if (typeof document === 'undefined') {
- return;
- }
-
- // Write
-
- if (arguments.length > 1) {
- attributes = extend({
- path: '/'
- }, api.defaults, attributes);
-
- if (typeof attributes.expires === 'number') {
- var expires = new Date();
- expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
- attributes.expires = expires;
- }
-
- try {
- result = JSON.stringify(value);
- if (/^[\{\[]/.test(result)) {
- value = result;
- }
- } catch (e) {}
-
- if (!converter.write) {
- value = encodeURIComponent(String(value))
- .replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
- } else {
- value = converter.write(value, key);
- }
-
- key = encodeURIComponent(String(key));
- key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
- key = key.replace(/[\(\)]/g, escape);
-
- return (document.cookie = [
- key, '=', value,
- attributes.expires ? '; expires=' + attributes.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
- attributes.path ? '; path=' + attributes.path : '',
- attributes.domain ? '; domain=' + attributes.domain : '',
- attributes.secure ? '; secure' : ''
- ].join(''));
- }
-
- // Read
-
- if (!key) {
- result = {};
- }
-
- // To prevent the for loop in the first place assign an empty array
- // in case there are no cookies at all. Also prevents odd result when
- // calling "get()"
- var cookies = document.cookie ? document.cookie.split('; ') : [];
- var rdecode = /(%[0-9A-Z]{2})+/g;
- var i = 0;
-
- for (; i < cookies.length; i++) {
- var parts = cookies[i].split('=');
- var cookie = parts.slice(1).join('=');
-
- if (cookie.charAt(0) === '"') {
- cookie = cookie.slice(1, -1);
- }
-
- try {
- var name = parts[0].replace(rdecode, decodeURIComponent);
- cookie = converter.read ?
- converter.read(cookie, name) : converter(cookie, name) ||
- cookie.replace(rdecode, decodeURIComponent);
-
- if (this.json) {
- try {
- cookie = JSON.parse(cookie);
- } catch (e) {}
- }
-
- if (key === name) {
- result = cookie;
- break;
- }
-
- if (!key) {
- result[name] = cookie;
- }
- } catch (e) {}
- }
-
- return result;
- }
-
- api.set = api;
- api.get = function (key) {
- return api.call(api, key);
- };
- api.getJSON = function () {
- return api.apply({
- json: true
- }, [].slice.call(arguments));
- };
- api.defaults = {};
-
- api.remove = function (key, attributes) {
- api(key, '', extend(attributes, {
- expires: -1
- }));
- };
-
- api.withConverter = init;
-
- return api;
- }
-
- return init(function () {});
-})); \ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 2500ddc6f6b..f254668646c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4450,6 +4450,10 @@ verror@1.3.6:
dependencies:
extsprintf "1.0.2"
+visibilityjs@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/visibilityjs/-/visibilityjs-1.2.4.tgz#bff8663da62c8c10ad4ee5ae6a1ae6fac4259d63"
+
vm-browserify@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73"
@@ -4464,9 +4468,9 @@ vue-resource@^0.9.3:
version "0.9.3"
resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-0.9.3.tgz#ab46e1c44ea219142dcc28ae4043b3b04c80959d"
-vue@^2.1.10:
- version "2.1.10"
- resolved "https://registry.yarnpkg.com/vue/-/vue-2.1.10.tgz#c9235ca48c7925137be5807832ac4e3ac180427b"
+vue@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-2.2.4.tgz#d0a3a050a80a12356d7950ae5a7b3131048209cc"
watchpack@^1.2.0:
version "1.2.1"